Promises resolve once.
A Promise represents a single future value. But real-world apps process streams of data: WebSocket messages, File Upload chunks, or infinite scrolling feeds.
Enter Async Iterators: A standard way to loop over future events as if they were an array.
02.
function* meets await
You can pause execution and return multiple values over time.
{'}'}
{'}'}
console.log(price);
{'}'}
03. Real-World Pattern: Paginated APIs
The most common use case isn't stock tickers; it's fetching large datasets. Instead of a recursive function that crashes the stack, use an async generator to "flatten" pages into a single stream of items.
let page = 1;
while (true) {'{'}
// Fetch chunk
const res = await api.get(`/users?page=${page}`);
if (res.data.length === 0) break;
// Yield items one by one
for (const user of res.data) {'{'}
yield user;
{'}'}
page++;
{'}'}
{'}'}
// Usage: Looks like a sync loop, behaves async!
for await (const user of fetchAllUsers()) {'{'}
processUser(user);
{'}'}
Deep Dive: The "Pull" Architecture
This is a Pull Stream. The consumer requests the next item (by calling next() implicit in the loop), and the producer computes it.
This is different from Push Streams (like RxJS Observables or standard DOM Events) where the producer blasts data at you whether you are ready or not.
Async Iterators are perfect for flow control because the producer awaits the consumer!
04. The Senior Engineer's Take
Memory Efficiency & Backpressure
Why use this over Promise.all()? Backpressure.
When you process a 1GB file line-by-line using a generator, you only keep one line in memory at a time. A standard file.read() would crash your V8 engine.