ECMAScript 2018: Asynchronous Iteration

It’s 2018 and it’s time for an update to the JavaScript standard. In January this year the TC39 committee released the latest changes to the ECMAScript standard, appropriately called ECMAScript 2018. One of the changes coming in this update is the introduction of Asynchronous Iteration and the for-await-of loop to iterate over asynchronous functions like generators and promises.

Synchronous Iteration

The form of iteration JS programmers are used to using is called Synchronous Iteration. It is used to iterate over things like an array or a function that has completes execution and returns a result.

It is possible for you to implement your own custom iterators for objects you create. The link above has an in depth explanation of how these iterators work and the different ways they can be used. I will summarize their usage like so:

  • Synchronous iteration requires the target object to be iterable
  • An object is iterable if it defines [Symbol.iterator]()
  • [Symbol.iterator]() must return an iterator
  • An iterator is an object that must at least define a function next()
  • The function next() must return an object in the form {value: $value, done: false} when iterating over values
  • The iterator is considered finished when the next() function returns {done: true}

Here is an example implementation of a custom iterable:

The problem with these synchronous iterators is that they can not be used to iterate over asynchronous operations, like calling a generator function (since generator functions call yield, they do not “finish” executing). To solve this problem the TC39 committee has added asynchronous iteration to the ECMAScript 2018 release.

Asynchronous Iteration

The new form of iteration, to use when iterating over asynchronous iterators is called asynchronous iteration. It is similar to its synchronous counterpart but instead of defining [Symbol.iterator]() the iterable must instead define [Symbol.asyncIterator](). The creation of asynchronous iterators is as follows:

  • Asynchronous iteration requires the target object to be iterable
  • An object is iterable if it defines [Symbol.asyncIterator]()
  • [Symbol.asyncIterator]() must return an AsyncIterator
  • An AsyncIterator is an object that must at least define a function next()
  • The function next() must return a promise that resolves with an IteratorResult
  • The IteratorResult is an object with a form {value: $value, done: false}
  • When the iterator is finished it must return {done: true}

However unlike the synchronous iterators, the asynchronous iterators cannot be used in a standard for-of loop. Instead they must use the new for-await-of loop. I have created an implementation of the synchronous iterator example above using asynchronous iterators below. Notice how the next() function in the iterable return a Promise instead of an object like in the first example. Also worth noting is that we have to use an asynchronous function to await for the result of the asynchronous iteration before we can update the page.

While the example is clearly more involved than the first, hopefully you can see that the asynchronous iterators now give developers the power to iterate over Promises and generator functions.