Categories
Modern JavaScript

Best Features of ES2018 — Async Generators

Spread the love

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at the best features of ES2018.

yield* in Async Generators

yield* in async generator works like how they work with normal generators.

If we have:

async function* gen() {
  yield 'foo';
  yield 'bar';
  return 'baz';
}

Then we can use it by writing:

(async function() {
  for await (const x of gen()) {
    console.log(x);
  }
})();

And we get:

foo
bar

logged.

Like with normal generators, the returned value is ignored by the for-of loop.

The operand of yield* can be any async iterable.

Sync iterables are automatically converted to async ones like with for-await-of.

Async Iteration Errors

In normal generators, next can throw exceptions.

With async generators, next can reject promises it returns.

For instance, we can write:

async function* asyncGen() {
  throw new Error('error');
}

to throw an error in an async generator.

Then we can catch the error by writing:

asyncGen().next()
  .catch(err => console.log(err));

We get the error object logged in the catch callback.

next return the rejected promise.

Different Between Async Function and Async Generator Function

An async function returns immediately with a promise.

The promise is fulfilled with return or rejected with throw .

So we can either write:

(async function () {
    return 'foo';
})()
.then(x => console.log(x));

or:

(async function() {
  throw new Error('error');
})()
.catch(x => console.log(x));

Async generators return immediately with an async iterable.

And each call of next returns a promise with yield x to fulfill the current promise with {value: x, done: false} .

It can also throw an error with the current promise with err .

So we can write:

async function* genFn() {
  yield 'foo';
}
const gen = genFn();
gen.next().then(x => console.log(x));

Then x is {value: “foo”, done: false} .

Turning an Async Iterable into an Array

We can turn an async variable into an array by looping through the async iterable and then pushing a value into a regular array.

For instance, we can write:

async function convertToArr(asyncIterable) {
  const result = [];
  const iterator = asyncIterable[Symbol.asyncIterator]();
  for await (const v of iterator) {
    result.push(v);
  }
  return result;
}

to create a function that takes an asyncIterable and then push the items into an array and return it.

It returns a promise that resolved to the result array.

Then we can use it by writing:

async function* genFn() {
  yield 'foo';
  yield 'bar';
}

(async () => {
  const arr = await convertToArr(genFn());
  console.log(arr);
})()

We created an async generator and passes the returned result of it to our convertToArr function that we created earlier.

Since it returns a promise that resolves to an array, we’ll see the value when we log it.

Internal Properties of Async Generators

Async generators have 2 internal properties.

[[AsyncGeneratorState]] has the state the generator is currently in.

It can be "suspendedStart", "suspendedYield", "executing", "completed" or undefined .

It’s undefined if it’s not fully initialized.

[[AsyncGeneratorQueue]] holds the pending invocation of next , throw or return .

Each queue has 2 fields, which are [[Completion]] and [[Capability]] .

[[Completion]] has the parameter for next , throw , or return that leads to the entry to be enqueued.

next , throw , or return indicates the method that created the entry and determines what happens after dequeuing.

[[Capability]] is the capability of the promise.

Conclusion

yield *can be used with async generators to call another generator.

There are also big differences between async functions and async generators.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *