How do async iterators and for-await-of work? Implement an async generator that paginates an API.
Understanding Symbol.asyncIterator, async generators, and sequential consumption of streamed/paginated data.
A sync iterator implements `[Symbol.iterator]()` returning an object with `next()` -> `{value, done}`. An ASYNC iterator implements `[Symbol.asyncIterator]()` whose `next()` returns a PROMISE of `{value, done}` — so each step can await I/O. `for await (const x of source)` consumes it: on each turn it awaits source.next(), unwraps the promise, and runs the body, pausing between items. It works over async iterables, sync iterables of promises, and — importantly — async generators. An async generator (`async function*`) is the ergonomic way to build one: `yield` produces a value, and you can `await` between yields, so it models a pull-based stream where the consumer sets the pace (natural backpressure — the next page isn't fetched until the consumer asks). The canonical use is pagination: yield items page by page, fetching the next page lazily only when the consumer has drained the current one. This keeps memory flat regardless of total size and lets the consumer `break` early to stop fetching. `for await` also propagates rejections (wrap in try/catch) and respects `return()`/`break` to clean up. Node streams and fetch response bodies are async iterables, so you can `for await (const chunk of stream)`.
Streaming large/paginated datasets, consuming Node streams or ReadableStreams, and any pull-based flow needing per-item await with backpressure.
Memory O(page) instead of O(total); consumer-paced.