Array.fromAsync()

The async sibling of Array.from. It accepts async iterables, awaits every element, and — unlike Promise.all — pulls them strictly in order.

Array static methodES2024Live demo
Common call
const rows = await Array.fromAsync(stream)
Returns
a Promise for an array — always awaited
Replaces
const out = []; for await (const x of src) out.push(x);
Watch out
it awaits IN ORDER; Promise.all does not
Array.fromAsync(source[, mapFn[, thisArg]])
→ Promise<Array>

Demo

Live evaluation
Try:
Inputs
jsonstringJSON list of [outcome, value, ms]
Output
await Array.fromAsync(JSON.parse('[["ok",1,30],["ok",2,10]]').map(([k, v, ms]) => new Promise((res, rej) => setTimeout(() => k === 'ok' ? res(v) : rej(new Error(v)), ms))))
Pending…

Each input is [outcome, value, delay in ms]: 'ok' fulfils with the value, anything else rejects with an Error carrying it. Every element is awaited and the resolved values are collected in input order — where plain Array.from would hand back the promises themselves, unawaited. A rejection anywhere rejects the whole call. Because these promises were all created up front they run concurrently; with a lazy source such as an async generator, fromAsync would pull them strictly one at a time.

Parameters

NameTypeRequiredDescription
sourceasync iterable | iterable | array-likeyesAn async iterable, a sync iterable, or an array-like. Sync sources still have each element awaited.
mapFnFunctionno (none)Called as mapFn(element, index) after the element is awaited. May itself be async — its result is awaited too.
thisArganyno (undefined)Value of `this` inside mapFn.

Return value

Promise<Array> — A Promise resolving to a new array. Rejects with the first error thrown by the source, the mapFn, or any element that rejects.

Common patterns

Collect an async stream
The main reason the method exists.
const chunks = await Array.fromAsync(readable);
Await an array of promises in order
Where Promise.all would resolve them in any order.
const results = await Array.fromAsync(promises);
Map asynchronously while collecting
The mapFn may be async; its result is awaited.
const bodies = await Array.fromAsync(urls, u => fetch(u).then(r => r.text()));

Examples

1. From an async generator
await Array.fromAsync(gen())
Returns
[1, 2]
2. Awaits sync promises too
await Array.fromAsync([Promise.resolve(1)])
Returns
[1]
3. Array.from does not
Array.from([Promise.resolve(1)])
Returns
[Promise]
4. With an async mapFn
await Array.fromAsync([1, 2], async x => x * 2)
Returns
[2, 4]
5. Array-like still works
await Array.fromAsync({length: 2})
Returns
[undefined, undefined]
6. It returns a Promise
Array.fromAsync([1]) instanceof Promise
Returns
true

Pitfalls

1. Forgetting to await it
The return value is a Promise, never an array. Without await you get a Promise object whose length is undefined and whose array methods do not exist — and nothing throws at the point of the mistake.
A Promise, not an array
const a = Array.fromAsync(src);
a.length
undefined
Await it
const a = await Array.fromAsync(src);
a real array
2. It is not a faster Promise.all
fromAsync awaits elements one at a time, in order. For an array of promises that were already created this costs nothing — they are all in flight already — but for a lazy source such as an async generator, each element is only requested after the previous one settles, so the total time is the SUM of the waits rather than the longest.
Serial over a lazy source
await Array.fromAsync(slowGenerator())
sum of every delay
Concurrent
await Promise.all(urls.map(fetch))
as slow as the slowest
3. One rejection abandons the rest
Like Promise.all, the first rejection rejects the whole thing, and with a lazy source the remaining elements are never even requested. There is no allSettled equivalent — map to a settled wrapper yourself if you need every result.
Loses the successes
await Array.fromAsync(mixed)
rejects on the first failure
Capture each outcome
await Array.fromAsync(src, p =>
  p.then(v => ({ok: true, v}), e => ({ok: false, e})));
every result kept
4. An infinite async source never resolves
It collects everything into memory before resolving, so an endless stream — a websocket, a tailed log, a polling generator — hangs forever and grows without bound. Bound it with a take-style helper or a plain for await with a break.
Never settles
await Array.fromAsync(endlessStream)
hangs, memory grows
Stop yourself
for await (const x of endlessStream) {
  if (done(x)) break;
}
bounded
5. ES2024 — check your runtime
Node 22+ and recent browsers. It is trivially polyfilled by a for-await loop, which is also what to write if you need to support anything older.
Missing
Array.fromAsync(src)
TypeError: Array.fromAsync is not a function
Write the loop
const out = [];
for await (const x of src) out.push(x);
same result

When to use

Use it
  • Draining a finite async iterable into an array
  • Collecting the output of an async generator
  • Awaiting a list of values where order of resolution matters
  • Mapping asynchronously while collecting, in one pass
Reach for something else
  • You want maximum concurrency → Promise.all over a mapped array
  • You need every outcome including failures → Promise.allSettled
  • The source is synchronous and holds no promises → Array.from
  • The stream is infinite or huge → for await with a break, and process as you go

Notes

Complexity
O(n), plus the awaited time of every element
Return
A Promise for a new array; the source is only consumed, never modified
CPython impl
V8: Builtins-array-fromasync.tq
Memory
The entire result is buffered in memory before the Promise resolves
Thread-safe
Single-threaded; consuming an async iterator twice concurrently is a mistake

FAQ

Promise.all takes an array of promises that are already running and waits for all of them concurrently. Array.fromAsync pulls from an iterable one element at a time and awaits each before asking for the next. Over an array of existing promises the two finish together; over a lazy async generator, fromAsync is genuinely serial because nothing starts until it is requested.

await Promise.all(promises);       // concurrent
await Array.fromAsync(promises);   // in order, same elapsed time here
await Array.fromAsync(gen());      // strictly one at a time

History

ES2015
Array.from added for sync iterables and array-likes.
ES2024
Array.fromAsync added, completing the pair for async iterables.