Async Iteration with JavaScript
Tags: javascript, tutorial, webdev, beginners
Published: 2020-02-01 02:34:59

**Subscribe to my email list now at http://jauyeung.net/subscribe/**

**Follow me on Twitter at https://twitter.com/AuMayeung**

**Many more articles at https://medium.com/@hohanga**

**Even more articles at http://thewebdev.info/**

Asynchronous code is a very important part of JavaScript. We need to write lots of asynchronous code, since JavaScript is a single-threaded language. If we run everything line by line, then code that takes longer to run will hold up the program, causing it to freeze.

To write asynchronous code easily, JavaScript has promises, which are chainable and can be run in sequence without nesting.

ES2017 introduced the `async` and `await` syntax for chaining promises, which makes everything easier. However, there was still no way to run promises sequentially, iterating them in sequence. This means that running lots of promises in a sequence is still a problem with no solution.

Fortunately, in ES2018, we finally have a `for-await-of` loop that we can use with `async` functions that were introduced in ES2017. It works with any iterable objects, like the synchronous `for...of` loop. This means that we can iterate through objects like Maps, Sets, NodeLists, and the `arguments` object with it.

Also, it can any object with the `Symbol.iterator` method, which returns a generator to let us do the iteration.

* * *

for-await-of Loop
=================

We can use the `for-await-of` loop as follows:

```javascript

let promises = [];
for (let i = 1; i <= 10; i++) {
  promises.push(new Promise((resolve) => {
    setTimeout(() => resolve(i), 1000);
  }));
}
(async () => {
  for await (let p of promises) {
    const val = await p;
    console.log(val);
  }
})();
```

In the example above, we added ten promises by defining them in the `for` loop and then pushing them to the `promises` array. Then we run the promises by defining an `async` function and then using the `for-await-of` loop to loop through each promise and run them one at a time.

The `await` in the `for-await-of` loop will get us the resolved value from the promise and the `console.log` will log the resolved value.

Like other `async` functions, it can only return promises.

The syntax of the `for-await-of` loop is the same as any other `for...of` loop. The `let p` is the variable declaration and `promises` is the `promises` array we’re iterating through.

The code above works like `Promise.all`, the code is run in parallel, so we see one to ten after one second.

* * *

Async Iterables
===============

Alternatively, we can define our own asynchronous iterable object if we define an object with the `Symbol.asyncIterator` method. It’s a special method that returns a generator that lets us iterate through it with the `for-await-of` loop.

For example, we can write:

```javascript
let asyncIterable = {
  [Symbol.asyncIterator]() {
    return {
      i: 1,
      next() {
        if (this.i <= 10) {
          return new Promise((resolve) => {
            setTimeout(() => resolve({
              value: this.i++,
              done: false
            }), 1000);
          });
        }
        return Promise.resolve({
          done: true
        });
      }
    };
  }
};
(async () => {
  for await (let p of asyncIterable) {
    const val = await p;
    console.log(val);
  }
})();
```

The code above will return a generator, which returns a new promise as the `for-await-of` loop runs.

In the `Symbol.asyncIterator` method, we have a `next` function, which is required if we want to make an asynchronous iterable object. Also, we have the `i` property to keep track of what we iterated through.

In the promise that we return, we have the `setTimeout` with a callback function that resolves to an object with the `value` we want to resolve, and the property `done`. The `done` property is `false` if the iterator should keep on returning new values for the `for-await-of` loop and `true` otherwise.

* * *

Async Generator
===============

We can make the code shorter with the `yield` keyword to make a generator function to return a generator. Generators are another kind of iterable object which can be used with the `for-await-of` loop.

For example, we can write:

The keyword `function*` denotes that our function only returns a generator. `async` means that the generator runs asynchronously. Inside the `asyncGenerator` function, we have the `yield` keyword. The `yield` keyword gets us the next item from the generator as we loop through the generator with the `for-await-of` loop.

The `yield` keyword only works with top-level code. It doesn’t work inside callback functions.

If we run the code above, we should get one to ten outputted.

If we write something like this:

```javascript
async function* asyncGenerator() {  
  for (let i = 1; i <= 10; i++) {  
    setTimeout(() => {  
      yield i;  
    }, 1000);  
  }  
}
```

We’ll get a `SyntaxError`.

An object can be made iterable with if we define a `Symbol.asyncIterator` method which is set to a generator function.

For example, we can write:

```javascript
async function* asyncGenerator() {
  for (let i = 1; i <= 10; i++) {
    yield i;
  }
}
(async () => {
  for await (let p of asyncGenerator()) {
    const val = await p;
    console.log(val);
  }
})();
```

Then we get one to ten logged in the `console.log` output.

It works like the `asyncIterable` object we defined above, except that the code is much shorter. Also, we can’t use the `yield` keyword on callbacks so we have to write it as we have above in that case.

Finally, we can write the following to loop through promises directly:

The `await` keyword actually waits for each promise to resolve until running the next one, so we get each number showing up after one second when we run the code.

Asynchronous iteration solves the promise of running many promises sequentially. This feature is available in ES2018 or later with the introduction of the `for-await-of` loop and asynchronous iterators and generators. With these the issue of iterating through asynchronous code has been solved.