You're collecting async iterable results with a for-await loop. Array.fromAsync does it in one call.
A for-await loop that pushes into an array, or the `await Promise.all([...asyncIterable])` workaround that silently fails — `Array.fromAsync` replaces both with a single awaited call. It's Baseline 2024.

When you have an async iterable — a ReadableStream, a generator that fetches paginated results, a database cursor — and you need its values in a plain array, you reach for a loop.
That works. But it's four lines of ceremony for "give me an array of everything this produces." Array.fromAsync is the one-liner that's been missing.
If you've tried [...asyncIterable], you know it throws. Spread syntax works with synchronous iterables only. The await Promise.all([...asyncIterable]) trick fails too — the spread happens before await, which means JavaScript tries to spread a synchronous iterator that doesn't exist on the async source.
The for-await loop is the correct fallback. But it's exactly the kind of boilerplate a standard library should absorb.
That's it. It pulls one value from the source, awaits it, stores it, then pulls the next — returning a fully-populated plain array when the source is exhausted.
Like Array.from(), it accepts a mapping function as the second argument:
The mapper runs after each value has been awaited. You can return a promise from the mapper too — Array.fromAsync awaits that as well before moving on.
Array.fromAsync accepts three kinds of input:
Async iterables — anything with a [Symbol.asyncIterator]() method. This is the main use case: generators, streams, cursors, any API that produces values lazily over time.
Sync iterables with an async mapper — this replaces the common await Promise.all(array.map(async fn)) pattern, with one key difference explained below.
Array-like objects — objects with numeric indices and a length property, same as Array.from.
This is the most important thing to understand about Array.fromAsync: it processes values one at a time, in order. It awaits each value fully before pulling the next.
This is different from Promise.all, which fires all promises concurrently and waits for all of them together.
When you pass a sync array with an async mapper, Array.fromAsync fetches item 1, awaits the result, stores it, then fetches item 2. There's no parallelism inside the pipeline. If you're mapping over a known array and want concurrent fetches, Promise.all is still the right tool.
The sequential behavior is intentional for lazy sources: a generator or stream doesn't know what to produce next until you ask — you can't fan out requests that haven't been decided yet.
Async generators are where Array.fromAsync earns its place. Consider a paginated API client:
Same result. The generator drives pagination and Array.fromAsync collects everything, stopping when the generator returns.
Array.fromAsync is Baseline 2024: Chrome 121 (January 2024), Firefox 119 (October 2023), Safari 17.4 (March 2024), Node.js 22. For older targets, core-js 3.38+ includes a polyfill, and the manual for-await loop is always a valid fallback.
Runs right in your browser — poke at it and watch the concept react live.
Think it clicked? Take the 8-question quiz →
Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.
Search your codebase for for await ... push patterns that end with the array being returned or used. Each one is a direct candidate for await Array.fromAsync(source). When you're mapping an async function over a sync array and want sequential execution, Array.fromAsync(array, asyncMapper) replaces the manual loop. When you want concurrent execution over a known array, stick with await Promise.all(array.map(asyncMapper)). The distinction is sequential vs parallel — know which you need before reaching for either.
Thanks for reading! Let's stay connected:
- ⭐ GitHub — follow me and star the projects: github.com/parsajiravand
- 💬 Discord — join the frontend best-practices community: discord.gg/d9KRhuAwQ
- 📸 Instagram — frontend best practices, daily: @bestpractice___
- 💼 LinkedIn — linkedin.com/in/parsa-jiravand
- ✉️ Email (work & contract inquiries): bestpractice2026@gmail.com
Originally published on dev.to