JavaScript Async Iteration

Async iteration lets JavaScript consume values that become available over time through the async iterator protocol, commonly with `for await...of`. It is useful for streams, paginated data, and other asynchronous sequences where each next value may require awaiting.

An async iterable provides a `[Symbol.asyncIterator]()` method that returns an async iterator. That iterator exposes `next()`, which returns a promise resolving to an object shaped like `{ value, done }`.

`for await...of` consumes an async iterable one result at a time. Before each loop body runs, JavaScript awaits the promise returned by `next()`, so the body receives the resolved value rather than the pending promise.

Async iteration is distinct from ordinary iteration. A synchronous iterable uses `[Symbol.iterator]()` and synchronous `next()` results, while an async iterable uses `[Symbol.asyncIterator]()` and asynchronous results. `for await...of` can also consume ordinary synchronous iterables by adapting their yielded values, including promises.

An async generator is a convenient way to implement an async iterable. Declaring a function with `async function*` allows it to use `yield` while also using `await`, making it practical for producing values from asynchronous operations without manually implementing the iterator object.

Iteration is sequential by default: the next iteration is not requested until the previous `next()` result has been awaited. This is often desirable for streams or rate-sensitive sources, but independent asynchronous work may need a different concurrency strategy such as collecting promises and using `Promise.all`.

Key points

Interview questions

Does `for await...of` process iterations concurrently?

No, the loop normally waits for the current iteration's asynchronous step before moving to the next one. That makes it appropriate when order matters or when the producer controls when the next value is available. If several independent operations should start together, creating promises first and using `Promise.all` can provide concurrency instead.

What is the async iterator protocol in JavaScript?

An async iterable exposes `[Symbol.asyncIterator]()`, which returns an async iterator. The iterator has a `next()` method whose result is awaited and resolves to an object containing `value` and `done`. This protocol lets JavaScript represent sequences whose elements may become available asynchronously.

What does `for await...of` do?

`for await...of` consumes an iterable asynchronously, awaiting each iteration result before assigning its value to the loop variable. With an async iterable, this means awaiting the promise returned by each `next()` call. It is especially useful when values arrive incrementally, such as from an async generator or stream-like source.

How would you create an asynchronous sequence without manually implementing `next()`?

Use an async generator declared with `async function*`. Inside it, `yield` produces sequence elements, while `await` can pause until asynchronous work completes. The resulting generator object implements the async iteration protocol and can be consumed with `for await...of`.

Can `for await...of` consume a synchronous iterable?

Yes. `for await...of` can consume a synchronous iterable when an async iterator is not available, adapting its iteration results for asynchronous consumption. This also means values that are promises can be awaited as part of consumption. However, using `for await...of` on a purely synchronous sequence can introduce unnecessary asynchronous behavior compared with `for...of`.