Asyncadvanced
Async Iterators and for-await-of
Iterating over sequences of values that arrive asynchronously, such as paginated APIs or streamed data, using async generators and for-await-of.
An async iterator produces values that arrive over time, and for await...of consumes them one at a time.
for await...of is like reading letters arriving one by one over several days, versus for...of reading a stack already sitting on your desk.
Key Concepts
1
async function* pages(url) {
let next = url;
while (next) {
const res = await fetch(next);
const data = await res.json();
yield* data.items; // yield each item
next = data.nextPage;
}
}2
for await (const item of pages('/api/items')) {
process(item);
}3
What that buys. The consumer sees a flat sequence of items. Pagination — fetching, checking for a next
page, looping — is hidden in the generator.
4
Without it, the caller manages the cursor, and every consumer of that API repeats the same loop.
5
It is lazy, which is the memory argument. The next page is not fetched until the loop asks for it. A
million-item dataset is processed without ever holding a million items.
6
// eager -- everything in memory
const all = await fetchAllPages();
all.forEach(process);7
// lazy -- one page at a time
for await (const item of pages(url)) process(item);8
break stops the fetching, because the generator is suspended and never resumed. Finding what you need
on page two means pages three onward are never requested.
break
9
The distinction from a sync generator.
function* yields values now
async function* yields promises; for await unwraps them10
Node streams are async iterable, which is where most people meet this:
11
for await (const chunk of fs.createReadStream('big.log')) {
process(chunk);
}12
Reading a file larger than memory, line by line, with ordinary loop syntax.
13
The caution: it is sequential by design.
for await (const url of urls) {
await fetch(url); // one at a time
}14
If the items are independent and you want concurrency, this is the wrong tool — that is Promise.all over
a batch. for await is for a genuine sequence where order or backpressure matters.
Promise.allfor await
15
Cleanup runs on early exit. A finally inside the generator executes when the loop breaks, which is how
a stream or a connection gets closed.
finally
16
What the interviewer is probing.1. "When would you use an async iterator?" Probing: the use case. Stalls: "Instead of a
loop." Moves up: a sequence that arrives over time — paginated API results or a stream — consumed
without buffering it all.
17
2. "What does for-await-of do with an array of promises?" Probing: the behaviour. Stalls:
"Runs them in parallel." Moves up: it awaits each in turn, serialising them — use Promise.all when
they are independent.
18
3. "What makes an object async iterable?" Probing: the protocol. Stalls: "A then method."
Moves up: a Symbol.asyncIterator method returning an object whose next returns a promise of a
result.
19
4. "How do you write one easily?" Probing: the syntax. Stalls: "By hand." Moves up: an
async generator — async function* — where each yield becomes a value in the sequence.