February 4, 2022

Async iteration and generators

Asynchronous iteration allow us to iterate over data that comes asynchronously, on-demand. Like, for instance, when we download something chunk-by-chunk over a network. And asynchronous generators make it even more convenient.

Let’s see a simple example first, to grasp the syntax, and then review a real-life use case.

Recall iterables

Let’s recall the topic about iterables.

The idea is that we have an object, such as range here:

let range = {
  from: 1,
  to: 5
};

…And we’d like to use for..of loop on it, such as for(value of range), to get values from 1 to 5.

In other words, we want to add an iteration ability to the object.

That can be implemented using a special method with the name Symbol.iterator:

  • This method is called in by the for..of construct when the loop is started, and it should return an object with the next method.
  • For each iteration, the next() method is invoked for the next value.
  • The next() should return a value in the form {done: true/false, value:<loop value>}, where done:true means the end of the loop.

Here’s an implementation for the iterable range:

let range = {
  from: 1,
  to: 5,

  [Symbol.iterator]() { // called once, in the beginning of for..of
    return {
      current: this.from,
      last: this.to,

      next() { // called every iteration, to get the next value
        if (this.current <= this.last) {
          return { done: false, value: this.current++ };
        } else {
          return { done: true };
        }
      }
    };
  }
};

for(let value of range) {
  alert(value); // 1 then 2, then 3, then 4, then 5
}

If anything is unclear, please visit the chapter Iterables, it gives all the details about regular iterables.

Async iterables

Asynchronous iteration is needed when values come asynchronously: after setTimeout or another kind of delay.

The most common case is that the object needs to make a network request to deliver the next value, we’ll see a real-life example of it a bit later.

To make an object iterable asynchronously:

  1. Use Symbol.asyncIterator instead of Symbol.iterator.
  2. The next() method should return a promise (to be fulfilled with the next value).
    • The async keyword handles it, we can simply make async next().