Learn JavaScript async iterators and generators with runnable examples. Stream paginated APIs, process incoming data one chunk at a time, and use for await...of.
Async iterators let you consume asynchronous sequences one value at a time with clean syntax. for await...of is the async cousin of for...of — it awaits each next() call, pausing the loop until the next value arrives. Combined with async generators (async function*), they become the best tool for streaming paginated APIs, reading files line by line, and processing incoming websocket messages without buffering everything into memory.
An async generator yields values asynchronously. Each yield returns a promise to the consumer; the next yield waits for the consumer to call next() again. The generator body can await anything between yields.
for await (const x of asyncIterable) calls next(), awaits the result, unwraps .value, and runs the body. When next() returns { done: true }, the loop ends. Any rejection jumps to try/catch just like a regular await.
Fetch page 1, yield each item, fetch page 2, yield each item, repeat until no more pages. The consumer sees a flat stream; the generator handles pagination internally.
break inside for await...of calls the iterator's return() method, giving the generator a chance to clean up. This is how you stop streaming when you've found what you need.
Use async iterators for streaming data with unknown or large size: paginated APIs, line-by-line file reading, incoming messages over a socket, chunked HTTP responses. Use a plain array + Promise.all when the total count is small and known.
async function* paginate(pageSize) {
for (let page = 1; page <= 3; page++) {
// Simulate API call
await new Promise(r => setTimeout(r, 50));
const items = Array.from(
{ length: pageSize },
(_, i) => `page${page}-item${i + 1}`,
);
console.log(`fetched page ${page}`);
for (const item of items) yield item;
}
}
(async () => {
// Stream every item across all pages
for await (const item of paginate(3)) {
console.log("got:", item);
}
// Early exit
console.log("--- with break ---");
for await (const item of paginate(3)) {
console.log("scan:", item);
if (item === "page2-item2") break;
}
})();
Open this example in the TryJS playground to edit and run the code instantly in your browser — no signup needed.
An async iterator is an object whose next() method returns a promise for { value, done }. You consume it with for await...of, which awaits each step. Async generators (async function*) are the easiest way to build one.
Use for await...of when values arrive over time (streaming, pagination, sockets) or when the total count is unknown. Use Promise.all when you have a fixed list of independent promises and you want them all to run concurrently.
Yes — break works and also calls the iterator's return() method, which gives an async generator a chance to clean up (close connections, stop fetching). This makes 'first match wins' streaming searches clean and safe.
No — it runs serially by design. Each iteration waits for the previous one's await to settle. If you want parallelism over a stream, collect items in batches and Promise.all the batches.