liveBook · Manning

Async iterator

As explained in The Joy of JavaScript

An async iterator is an iterator interface for producing values asynchronously, typically yielding a sequence of promised results over time.

An async iterator produces a sequence of values whose next result may take time to arrive. Like a synchronous iterator, it reports a value and whether iteration is done. The difference is that its `next()` method returns a Promise that resolves to `{value, done}`. This lets a loop wait for each result without loading an entire data source at once. section 9.6 section 8.5

Ordinary `for...of` does not wait for asynchronous results. If three delayed promises are handled with `.then()` inside that loop, their callbacks can run in completion order rather than array order. A Promise chain can coordinate them, but `for await...of` expresses the waiting directly: section 8.5

Consume delayed values

```js for await (const value of [ delay('a', 500), delay('b', 100), delay('c', 200) ]) { console.log(value); } ``` The loop processes the results as `a`, `b`, then `c`, despite their different delays. The promises have already been created; the loop controls when each result is processed. section 8.5

To make an object asynchronously iterable, define its `Symbol.asyncIterator` method to return an async iterator. Each call to that iterator’s `next()` returns a Promise for the next `{value, done}` result; a result with `done: true` ends the sequence. `for await...of` consumes the object through this protocol. section 8.5 section 9.6

Listing 8.26 Iterator object that emits values with a provided delay
function delayedIterator(tasks) {
   return {
      next: function () {
         if (tasks.length) {
            const [value, time] = tasks.shift();                   #1
            return new Promise(resolve => {                        #2
               setTimeout(resolve, time, { value, done: false });
            });
         } else {
            return Promise.resolve({
               done: true                                          #3
            });
         }
      }
   };
}

A delayed iterator makes the protocol concrete: `next()` resolves to a value-bearing result while tasks remain, then resolves to a done result. section 8.5

File streams put the protocol to work. Iterating over a Node.js read stream yields chunks, so a large file can be processed as a moving window instead of being read wholly into memory. When records can cross chunk boundaries, retain the unfinished portion and combine it with the next chunk before parsing. section 8.5

An async generator offers another way to produce an asynchronously consumed sequence. A file-processing generator can yield parsed records as they become available, leaving consumers free to count or validate them. That separates reading and parsing from the operation performed on each record. section 9.2.3 section 9.6

Listing 9.9 Using generateBlocksFromFile as an async iterator
let result = 0;
for await (const block of generateBlocksFromFile('blocks.txt')) {      #1
   console.log('Counting block', block.hash);
   result++;
}
result; // 3

The counting loop consumes records from an async generator without containing the file-reading or parsing logic. section 9.2.3

How The Joy of JavaScript covers it

How deep it goes
Works through promised `{value, done}` results, `Symbol.asyncIterator`, `for await...of`, and chunked file processing.
Examples
Uses delayed promises, a custom delayed iterator, a chunked file reader, and an async generator of parsed records.
What it assumes
Assumes familiarity with callbacks and basic synchronous iterators while developing the Promise-based asynchronous flow.
What you can do after
Build it from scratch with `Symbol.asyncIterator`, or apply it to Node.js streams and async generators using `for await...of`.
How it connects
Connects synchronous iterators and Promises to `for await...of`, async generators, and streams.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of The Joy of JavaScript. The section links point to the parts of the book it draws on.