ReviseAlgo Logo

Asynchronous JavaScript

Async Iterators & for await...of

Master asynchronous iteration in JavaScript. Learn to implement Symbol.asyncIterator, write async generators, and process data streams using for await...of.

Last Updated: July 15, 2026 10 min read

1. Introduction

Standard iterators deal with synchronous data. To iterate over collections where elements are retrieved asynchronously (like streaming data from a network or loading files in chunks), JavaScript provides the Async Iterator protocol and the for await...of loop.

2. Why It Matters

Async iterators allow you to process data streams (like Node.js readable streams or paginated API requests) element-by-element as the data arrives, instead of waiting for the entire dataset to download, saving memory.

3. Real-World Analogy

Think of a Conveyor Belt at a Baggage Claim:

  • Standard Iterator (Synchronous Pile): All luggage is pre-unloaded and stacked in the lobby. You walk along the pile and inspect each bag. The bags are immediately available.
  • Async Iterator (Moving Conveyor Belt): You stand at the belt. The belt moves. You must wait (await) for each bag to arrive sequentially. If there is a delay in the baggage room, you wait at the belt until the next bag emerges (resolves the next promise). You process bags one-by-one as they arrive.

4. The Async Iterator Protocol

An object is async iterable if it implements a method with the key [Symbol.asyncIterator]. This method must return an async iterator whose next() method returns a Promise that resolves to an iterator result: { value, done }.

5. Asynchronous Generators

The easiest way to write custom async iterators is by using Asynchronous Generators (declared using async function*). Inside an async generator, you can use both the yield keyword (to return values) and the await keyword (to pause execution for asynchronous tasks).

6. Practical Example

This script demonstrates streaming lines from a readable text stream using an async generator:

7. Common Mistakes

  • Using standard for...of loops instead of for await...of: Standard for...of loops do not wait for the next Promise to resolve. Attempting to run them on async iterables returns Promise references directly or throws a TypeError.
  • Forgetting to handle stream terminations: Not setting the done: true flag inside your custom async iterator creates infinite loops that run indefinitely.

8. Quick Quiz

Q1: What does an async iterator's next() method return?

A) An object containing { value, done }

B) A Promise that resolves to an object containing { value, done }

Answer: B — The next() method of an async iterator returns a Promise that resolves to the standard { value, done } iteration result.

9. Scenario-Based Challenge

The Dynamic Log Tailer:

A Node.js server app monitors a local log file: watchLog(). When a new line is written, load the line asynchronously. Design an async generator that yields new log lines as they are written, allowing developers to process logs in real time.

10. Debugging Exercise

Identify and fix the syntax error in the following dynamic stream processor:

function* getMetricsStream() {
  // Objective: Yield metrics retrieved from API page calls
  const page1 = await fetch('/page1').then(r => r.json()); // syntax bug!
  yield page1;
  const page2 = await fetch('/page2').then(r => r.json());
  yield page2;
}
View Solution

Diagnosis: You cannot use the await keyword inside a standard generator function (function*). To use await, you must declare it as an asynchronous generator function (async function*).

Fix: Add the async modifier before the generator function definition:

async function* getMetricsStream() {
  const page1 = await fetch('/page1').then(r => r.json());
  yield page1;
  const page2 = await fetch('/page2').then(r => r.json());
  yield page2;
}

11. Interview Questions

🟢 Q1: Compare standard generators with asynchronous generators.

Answer:
Standard Generators (function*): Execute synchronously. The next() method returns a plain object: { value, done } immediately. You cannot use await inside the function body.
Asynchronous Generators (async function*): Execute asynchronously. The next() method returns a Promise that resolves to { value, done }. You can use both the yield and await keywords inside the function body.

12. Production Considerations

  • Stream Backpressure: When iterating over fast data streams using for await...of, ensure your processing logic in the loop is fast. If processing takes longer than the data arrival time, it can cause backpressure issues, consuming memory.