Paginated APIs, streams, file lines and event sources are all naturally async sequences.
Some sequences produce values over time: pages from an API, chunks from a network stream, lines from a file. Async iteration is the iteration protocol with promises: an async iterable has a [Symbol.asyncIterator]() method whose iterator's next() returns a promise of { value, done }.
Consume one with `for await...of` inside an async function or module:
for await (const chunk of stream) process(chunk);Each iteration waits for the next value; break, return or a thrown error close the iterator so the source can clean up. for await also accepts ordinary iterables of promises, awaiting each element in turn.
The easiest way to produce one is an async generator (async function*): you can await inside it and yield values as they become available. The consumer pulls values one at a time, so the generator only does work on demand — natural backpressure. Classic example: hiding pagination behind a flat stream of items, so callers never see page tokens.
Where you'll meet async iterables:
- Node.js readable streams,
readlineinterfaces andfs.promises.opendir. - Web
ReadableStreams in runtimes that support async-iterating them (Node.js, Deno and recent browsers). events.on(emitter, "data")in Node.js.
Array.fromAsync(asyncIterable) collects everything into an array.
Note that for await processes items sequentially. If items are independent and you want concurrency, gather them and use Promise.all — with a limit if there are many.
Example
const sleep = ms => new Promise(r => setTimeout(r, ms));
// Fake paginated API: 3 pages of 2 items
async function fetchPage(cursor = 0) {
await sleep(20);
const items = [`item${cursor * 2}`, `item${cursor * 2 + 1}`];
return { items, next: cursor < 2 ? cursor + 1 : null };
}
async function* allItems() {
let cursor = 0;
while (cursor !== null) {
const page = await fetchPage(cursor); // fetched only when needed
yield* page.items;
cursor = page.next;
}
}
for await (const item of allItems()) {
console.log(item);
if (item === "item3") break; // stops: page 3 is never fetched
}
const everything = await Array.fromAsync(allItems());
console.log(everything.length); // 6Remember
- Async iterables return promises from
next(); consume withfor await...of async function*can await and yield — great for pagination and streams- Iteration is sequential and pull-based; use
Promise.allfor concurrency
Watch out for
- Using
for awaitover independent requests and getting serial instead of parallel execution.