Prompt
Create an async generator that yields numbers 1 to 5 with a short delay (50 ms) between each. Write consume() that collects them with for-await-of and returns the array.
Solution
Async generators are pull-based streams: the consumer decides when to ask for the next value, so backpressure is built in — the producer does no work until next() is called. That's the natural shape for paginated APIs: yield each page as it arrives, and the consumer stops pulling when it has enough, so page 7 is never fetched if the caller breaks after page 2. Compare that to eagerly fetching everything and filtering afterwards.
Mechanics: async function* combines both protocols — each next() returns a promise of {value, done}, and for await...of is the loop that awaits each one in sequence. The generator body reads synchronously top-to-bottom while actually suspending twice per iteration: once at await (the delay), once at yield (waiting for the consumer to pull again). The protocol also includes return() — for await...of calls it when you break, so a finally block in the generator runs for cleanup (close the connection, abort the request). That cleanup guarantee is what makes generators safer than hand-rolled iterator objects.
Red flag: fetching all pages with Promise.all and then yielding them. It "works" but destroys the two things the pattern exists for — laziness and bounded memory — and signals you reached for a familiar tool instead of understanding the streaming contract.
Say it: "An async generator is a pull-based stream — next() returns a promise of {value, done}, the consumer controls pacing, and for-await-of calls return() on break so my finally cleanup always runs."