Progress pill
Advanced JavaScript

Async Iterators

  • Async generator functions
  • Why use async iterators?
  • Symbol.asyncIterator
You already learned about iterators and how we can use for...of to loop over arrays and other iterable things.
But what if the data we want to iterate over takes time to arrive?
Sometimes we want to loop over things that arrive asynchronously, like messages from a chat, lines from a file, or numbers from a slow source.
To do that, JavaScript gives us async iterators.

Async generator functions

The easiest way to create an async iterator is to use an async generator function.
We write it like this:
async function* generateNumbers() { yield 1 yield 2 yield 3 }
This looks just like a regular generator, but with async before it.
We can now use for await...of to consume the values:
async function run() { for await (const n of generateNumbers()) { console.log("Got number:", n) } console.log("Done!") } run()
This will print:
Got number: 1 Got number: 2 Got number: 3 Done!
So what’s the difference with a normal generator?
The difference is: we can now use await inside the generator.
Let’s make a delay helper again:
function wait(ms) { return new Promise(resolve => setTimeout(resolve, ms)) }
Now let’s yield numbers slowly:
async function* generateSlowNumbers() { await wait(1000) yield 1 await wait(1000) yield 2 await wait(1000) yield 3 }
Try using it:
async function run() { for await (const n of generateSlowNumbers()) { console.log("Got number:", n) } console.log("Done!") } run()

Why use async iterators?

Async iterators are useful when:
  • The values don't all arrive at once.
  • You want to handle them one at a time,* as they come.
  • You're working with Promises, and want to loop in a clean way.
For example, if you want to load messages from a chat server one by one, or download a large file in chunks, async iterators give you a way to write a for loop that works with delayed data.

Symbol.asyncIterator

We can also use async iterators in custom classes.
Here’s an example that produces numbers with a delay:
function wait(ms) { return new Promise(resolve => setTimeout(resolve, ms)) } class DelayedNumbers { constructor(numbers) { this.numbers = numbers } async *[Symbol.asyncIterator]() { for (const n of this.numbers) { await wait(1000) yield n } } }
We can now use for await...of just like before:
async function run() { const source = new DelayedNumbers([10, 20, 30]) for await (const n of source) { console.log("Received:", n) } console.log("All done!") } run()
This allows you to create objects that can be iterated over asynchronously