Array.fromAsync()
Array.fromAsync(items, mapFn?, thisArg?) Array.fromAsync() is a static method on Array that takes an async iterable, a sync iterable, or an array-like object and returns a Promise which resolves to a new Array. It is the asynchronous sibling of Array.from(), and the right tool when the source is a stream, an async generator, or any sequence of values that arrive over time.
Syntax
Array.fromAsync(items)
Array.fromAsync(items, mapFn)
Array.fromAsync(items, mapFn, thisArg)
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
items | AsyncIterable | Iterable | ArrayLike | required | Source to read values from. Async iterables are tried first, then sync iterables, then plain array-likes (objects with a length property and numeric keys). |
mapFn | (element, index) => any | undefined | Optional mapping function called once per element. For sync sources, both the input and the return value are awaited. For async sources, only the return value is awaited. |
thisArg | any | undefined | Value to use as this when executing mapFn. |
Return value
A Promise that resolves to a new Array instance. The promise rejects with the first rejection encountered during iteration. The result is always a real Array. It is never a typed array or subclass, unless Array.fromAsync is called on a different this, in which case the result is an instance of that constructor (see the “Generic factory” example below).
Examples
From an async generator
async function* gen() {
for (let i = 0; i < 3; i++) yield i;
}
const arr = await Array.fromAsync(gen());
console.log(arr);
// [0, 1, 2]
Async generators are the natural source for Array.fromAsync(). Each yield becomes one element of the result, and the returned promise only resolves after the generator finishes.
From a sync iterable of promises
function* genPromises() {
yield Promise.resolve(1);
yield Promise.resolve(2);
yield Promise.resolve(3);
}
const arr = await Array.fromAsync(genPromises());
console.log(arr);
// [1, 2, 3]
When the source is a sync iterable, Array.fromAsync() awaits each yielded value before pushing it. Non-promise values are awaited too, which keeps mixed sync and async results consistent.
From an array-like of promises
const arrLike = {
length: 3,
0: Promise.resolve("a"),
1: Promise.resolve("b"),
2: Promise.resolve("c"),
};
const arr = await Array.fromAsync(arrLike);
console.log(arr);
// ['a', 'b', 'c']
Plain objects with a length property count as array-likes. This is the same shape Array.from() accepts, which is useful when working with arguments or older browser APIs that hand back index-keyed objects.
With a mapping function
function delayed(v) {
return new Promise(r => setTimeout(() => r(v), 50));
}
const arr = await Array.fromAsync(
[delayed(1), delayed(2), delayed(3)],
v => delayed(v * 10),
);
console.log(arr);
// [10, 20, 30]
Both the elements and the values returned from mapFn are awaited. The mapping runs in order with the source, so the timing of the result mirrors a sequential pipeline.
Reading a ReadableStream
const stream = new ReadableStream({
async start(controller) {
controller.enqueue("hello");
controller.enqueue("world");
controller.close();
},
});
const arr = await Array.fromAsync(stream);
console.log(arr);
// ['hello', 'world']
This is the most useful real-world pattern. Any async iterable (including ReadableStream, TransformStream, response bodies from fetch, and async generator functions) can be drained into an array with one call. For a deeper look at streams, see the Streams API guide.
Generic factory: calling on a different constructor
function Buffer() {}
Buffer.fromAsync = Array.fromAsync;
const buf = await Buffer.fromAsync([1, 2, 3]);
buf instanceof Buffer; // true
buf.length; // 3
Mirroring Array.from(), you can assign Array.fromAsync to another constructor to get back instances of that constructor. Rare in application code, but useful for libraries that want a parallel to MyClass.from(...).
Array.fromAsync() vs Array.from() vs Promise.all()
Array.fromAsync() | Array.from() | Promise.all() | |
|---|---|---|---|
| Returns | Promise<Array> | Array | Promise<Array> |
| Accepts async iterables | yes | no | no |
| Accepts sync iterables | yes | yes | yes |
| Accepts array-likes | yes | yes | no |
| Order of resolution | sequential, lazy | n/a | concurrent, eager |
| Awaits yielded values | yes (sync sources) | no | n/a |
| Reaches for it when | streams, backpressure, async generators | sync conversion with optional map | independent parallel work |
The short version: use Array.fromAsync for things that arrive over time, Array.from for sync data that needs a one-pass mapping, and Promise.all for a fixed list of already-existing promises. For a refresher on the async iterable protocol that powers the async case, see the async iterators guide.
Common gotchas
The return is always a Promise, even for sync input
const set = new Set([1, 2, 3]);
const p = Array.fromAsync(set); // Promise, not [1, 2, 3]
// Works:
console.log([...await Array.fromAsync(set)]);
// [1, 2, 3]
// Throws at runtime:
console.log([...Array.fromAsync(set)]);
// TypeError: object is not iterable
Always await (or .then()) the result before spreading or using array methods on it. Spreading or calling methods like .map() on the raw Promise throws a TypeError, since a Promise has no spreadable iterator and no array prototype methods.
Array.fromAsync(asyncIter) is not the same as Array.fromAsync(asyncIter, e => e)
async function* g() {
yield Promise.resolve(1);
yield Promise.resolve(2);
}
await Array.fromAsync(g()); // [Promise(1), Promise(2)]
await Array.fromAsync(g(), e => e); // [1, 2]
For an async iterable, the bare call passes each yielded value through unchanged. Values are not awaited. Adding an identity mapFn changes the result, because mapFn’s output is awaited. This is the most surprising behavior in the API.
Sequential awaiting is a performance trap for independent work
function* makeIterable() {
for (let i = 0; i < 5; i++) {
yield new Promise(r => setTimeout(r, 100));
}
}
console.time("fromAsync");
await Array.fromAsync(makeIterable());
console.timeEnd("fromAsync");
// fromAsync: ~500ms (sequential)
console.time("Promise.all");
await Promise.all([...makeIterable()]);
console.timeEnd("Promise.all");
// Promise.all: ~100ms (concurrent)
Array.fromAsync pulls one value at a time, which is the right behavior for backpressure-sensitive sources. For a fixed list of independent promises, Promise.all is faster because everything runs at once. Pick the tool that matches the workload.
Iterator cleanup is not run on rejection for sync sources
function* g() {
try {
yield 1;
yield Promise.reject(new Error("boom"));
} finally {
console.log("cleanup"); // never runs
}
}
try { await Array.fromAsync(g()); } catch (e) { console.error(e.message); }
// "boom"
Like for await...of over a sync iterable, the iterator’s return() method is not called when the loop exits early. If the source has a finally block, drive the iteration manually with for...of and await each value if you need cleanup.
Browser and runtime support
Array.fromAsync() is available in all modern browsers (Baseline 2024) and Node.js 22+. For older runtimes, core-js ships a polyfill.