Topics

Jump to a topic.

Search questions

Find a question by title, tag, or summary.

Array-likes vs iterables: how do Array.from and Array.fromAsync work?

Questions in this chapter (7)
  • Draft
  • medium
  • Askedsometimes
  • Verified October 2026

Answer

An iterable implements [Symbol.iterator]() (strings, Set, Map, generators, arguments). An array-like is any object with a length and indexed properties ({ length: 2, 0: 'a', 1: 'b' }). Only iterables work with spread and for...of; [...{ length: 2 }] throws a TypeError.

Array.from(items, mapper?, thisArg?):

  1. Throws if mapper is given and not callable.
  2. If items has a Symbol.iterator method, steps the iterator and collects the values.
  3. Otherwise treats items as an array-like: reads length, then items[0..length-1]. Missing indices become undefined (no holes).
  4. The mapper receives (value, index) only and runs as the values are produced, so there is no intermediate array.

items of null or undefined throw a TypeError; numbers and booleans give []. If this is a constructor, it is used to build the result (so subclasses work). Array.of(...items) is the sibling that builds from arguments: Array.of(7) is [7], while Array(7) is seven holes.

Array.from({ length: n }, fn) is the idiomatic way to build n real elements. new Array(n) creates holes, which map and forEach skip.

Array.fromAsync(items, mapper?, thisArg?) returns a Promise of an array. It checks Symbol.asyncIterator first, then Symbol.iterator, then falls back to an array-like. It processes one item at a time: it requests the next item, awaits it, and only then asks for the following one. The mapper's result is awaited, so an async mapper works. For sync iterables and array-likes each element is awaited too. It does not run work in parallel: for that, start the promises first and use Promise.all.

Availability: Node 24.19 has it. MDN marks it Baseline "Widely available" (since January 2024), and the proposal is Stage 4. Check your target runtimes for older environments.

Example

const sleep = (ms, value) => new Promise((resolve) => setTimeout(resolve, ms, value));

console.log(Array.from('héy'));                          // → [ 'h', 'é', 'y' ]
console.log(Array.from(new Set([1, 1, 2])));             // → [ 1, 2 ]
console.log(Array.from({ length: 3, 0: 'a', 2: 'c' }));  // → [ 'a', undefined, 'c' ]
console.log(Array.from({ length: 3 }, (_, i) => i * 2)); // → [ 0, 2, 4 ]
console.log(Array.of(7), Array(3));                      // → [ 7 ] [ <3 empty items> ]

async function* ticks() {
  for (let i = 1; i <= 3; i++) {
    await sleep(50);
    yield i;
  }
}
const timed = async (label, promise) => {
  const start = performance.now();
  const result = await promise;
  console.log(label, result, Math.round((performance.now() - start) / 50) * 50, 'ms');
};

await timed('async generator', Array.fromAsync(ticks()));
// → async generator [ 1, 2, 3 ] 150 ms
const tasks = [1, 2, 3].map((n) => () => sleep(50, n));
await timed('fromAsync', Array.fromAsync(tasks, (task) => task()));
// → fromAsync [ 1, 2, 3 ] 150 ms
await timed('Promise.all', Promise.all(tasks.map((task) => task())));
// → Promise.all [ 1, 2, 3 ] 50 ms

Pitfalls

  • Spreading an array-like. [...arrayLike] needs an iterator; Array.from(arrayLike) doesn't.
  • new Array(n).map(fn) skipping every slot. Use Array.from({ length: n }, fn).
  • Expecting Array.fromAsync to be concurrent. It waits for each item before requesting the next, so a mapper that starts slow work runs it in sequence. Use Promise.all for parallel work.
  • Passing Array.from(x, parseInt)-style functions with extra parameters. The mapper gets (value, index).

Follow-ups

Why prefer Array.from(iterable) over [...iterable]?
For iterables they give the same array, so it is a style choice. Array.from also accepts array-likes and a mapper, and can be called on a subclass.
What happens if Array.fromAsync input rejects?
The returned promise rejects with that error and the remaining items are not processed.
Is the result of Array.from a deep copy?
No: it is a shallow copy of the element values.

Exercises

Predict the output

console.log(Array.from({ length: 2, 0: 'a', 1: 'b' }, (v, i) => v + i));
console.log(Array.from(new Map([['k', 1]])));
console.log(await Array.fromAsync([Promise.resolve(1), 2], (n) => n * 2));