jsguides

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

ParameterTypeDefaultDescription
itemsAsyncIterable | Iterable | ArrayLikerequiredSource 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) => anyundefinedOptional 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.
thisArganyundefinedValue 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()
ReturnsPromise<Array>ArrayPromise<Array>
Accepts async iterablesyesnono
Accepts sync iterablesyesyesyes
Accepts array-likesyesyesno
Order of resolutionsequential, lazyn/aconcurrent, eager
Awaits yielded valuesyes (sync sources)non/a
Reaches for it whenstreams, backpressure, async generatorssync conversion with optional mapindependent 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.

See Also