Easy Introduction to JavaScript Generators
Tags: javascript, tutorial, beginners, webdev
Published: 2020-01-26 02:36:28

**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/**

In JavaScript, generators are special functions that return a generator object. The generator object contains the `next` value of an iterable object. It’s used for letting us iterate through a collection of objects by using the generator function in a `for...of` loop. This means the generator function returned conforms to the iterable protocol.

Synchronous Generators
======================

Anything that conforms to the iterable protocol can be iterated through by a `for...of` loop. Objects like `Array` or `Map` conform to this protocol. Generator functions also conform to the iterator protocol. This means that it produces a sequence of values in a standard way. It implements the `next` function which returns an object at least 2 properties — `done` and `value`. The `done` property is a boolean value that returns `true` with the iterator is past the end of the iterate sequence. If it can produce the next item in the sequence, then it’s `false`. `value` is the item returned by the iterator. If `done` is `true` then `value` can be omitted. The `next` method always returns an object with the 2 properties above. If non-object values are returned then `TypeError` will be thrown.

To write a generator function, we use the following code:

```javascript
function* strGen() { 
  yield 'a';
  yield 'b';
  yield 'c';
}

const g = strGen();
for (let letter of g){
  console.log(letter)
}
```

The asterisk after the function keyword denotes that the function is a generator function. Generator functions will only return generator objects. With generator functions, the `next` function is generated automatically. A generator also has a `return` function to return the given value and end the generator, and a `throw` function to throw an error and also end the generator unless the error is caught within the generator. To return the next value from a generator, we use the `yield` keyword. Each time a `yield` statement is called, the generator is paused until the `next` value is requested again.

When the above example is execute, we get ‘a’, ‘b’, and ‘c’ logged because the generator is run in the `for...of` loop. Each time it’s run the next `yield` statement is called, returning the next value from the list of the `yield` statements.

We can also write a generator that generates infinite values. We can have an infinite loop inside the generator to keep returning new values. Because the `yield` statement doesn’t run until the next value is requested, we can keep an infinite loop running without crashing the browser. For example, we can write:

```javascript
function* genNum() {
  let index = 0;
  while(true){
    yield index += 2;
  }
}
const gen = genNum();
console.log(gen.next().value);
console.log(gen.next().value);
console.log(gen.next().value);
```

As you can see, we can use a loop to repeatedly run `yield`. The `yield` statement must be run at the top level of the code. That means they can’t be nested inside another callback functions. The `next` function is automatically included with the generator object that is yielded to get the next value from the generator.

The `return` method is called when the iterator ends. That is, when the last value is obtained or when an error is thrown with the `thrown` method. If we don’t want it to end, we can wrap the `yield` statements within the `try...finally` clause like in the following code:

```javascript
function* genFn() {
  try {
    yield;
  } finally {
    yield 'Keep running';
  }
}
```

When the `throw` method is called when running the generator, the error will stop the generator unless the error is caught within the generator function. To catch, throw, and catch the error we can write something like the following code:

```javascript
function* genFn() {
  try {
    console.log('Start');
    yield; // (A)
  } catch (error) {
    console.log(`Caught: ${error}`);
  }
}
const g = genFn();
g.next();
g.throw(new Error('Error'))
```

As you can see, if we run the code above, we can see that ‘Start’ is logged when the first line is run since we’re just getting the first value from the generator object, which is the `g.next()` line. Then the `g.throw(new Error('Error'))` line is run which throws an error, which is logged inside the `catch` clause.

With generator functions, we can also call other generator functions inside it with the `yield*` keyword. The following example won’t work:

```javascript
function* genFn() {
  yield 'a'
}
function* genFnToCallgenFn() {
  while (true) {
    yield genFn();
  }
}
const g = genFnToCallgenFn()
console.log(g.next())
console.log(g.next())
```

As you can see, if we run the code above, the `value` property logged is the generator function, which isn’t what we want. This is because the `yield` keyword did not retrieve values from other generators directly. This is where the `yield*` keyword is useful. If we replace `yield genFn();` with `yield* genFn();`, then the values from the generator returned by `genFn` will be retrieved. In this case, it will keep getting the string ‘a’. For example, if we instead run the following code:

```
function* genFn() {
  yield 'a'
}
function* genFnToCallgenFn() {
  while (true) {
    yield* genFn();
  }
}
const g = genFnToCallgenFn()
console.log(g.next())
console.log(g.next())
```

We will see that the `value` property in both objects logged has the `value` property set to ‘a’.

With generators, we can write an iterative method to recursively traverse a tree with little effort. For example, we can write the following:

```javascript
class Tree{
  constructor(value, left=null, center=null, right=null) {
    this.value = value;
    this.left = left;
    this.center = center;
    this.right = right;
  }

  *[Symbol.iterator]() {
    yield this.value;
    if (this.left) {
      yield* this.left;
    }
    if (this.center) {
      yield* this.center;
    }
    if (this.right) {
      yield* this.right;
    }
  }
}
```

In the code above, the only method we have is a generator which returns the left, center, and right node of the current node of the tree. Note that we used the `yield*` keyword instead of `yield` because JavaScript classes are generator functions, and our class is a generator function since we have the special function denoted by the `Symbol.iterator` symbol, which means that the class will create a generator.

Symbols are new to ES2015. It is a unique and immutable identifier. Once you created it, it cannot be copied. Every time you create a new Symbol, it is a unique one. It’s mainly used for unique identifiers in an object. It’s a Symbol’s only purpose.

There are some static properties and methods of its own that expose the global symbol registry. It is like a built-in object, but it doesn’t have a constructor, so we can’t write `new Symbol` to construct a Symbol object with the `new` keyword.

`Symbol.iterator` is a special symbol that denotes that the function is an iterator. It’s built in to the JavaScript standard library.

If we have the define following code, then we can built the tree data structure:

```javascript
const tree = new Tree('a',
  new Tree('b',
    new Tree('c'),
    new Tree('d'),
    new Tree('e')
  ),
  new Tree('f'),
  new Tree('g',
    new Tree('h'),
    new Tree('i'),
    new Tree('j')
  )
);
```

Then when we run:

```javascript
for (const str of tree) {  
    console.log(str);  
}
```

We get all the values of the tree logged in the same order that we defined it. Defining recursive data structures is much easier than without generator functions.

We can mix `yield` and `yield*` in one generator function. For example, we can write:

```javascript
function* genFn() {
  yield 'a'
}
function* genFnToCallgenFn() {
  yield 'Start';
  while (true) {
    yield* genFn();
  }
}
const g = genFnToCallgenFn()
console.log(g.next())
console.log(g.next())
console.log(g.next())
console.log(g.next())
```

If we run the code above, we get ‘Start’ as the `value` property of the first item returned by `g.next()`. Then the other items logged all have ‘a’ as the `value` property.

We can also use the `return` statement to return the last value that you want to return from the iterator. It acts exactly like the last `yield` statement in a generator function. For example, we can write:

```javascript
function* genFn() {
  yield 'a';
  return 'result';
}
const g = genFn()
console.log(g.next())
console.log(g.next())
console.log(g.next())
console.log(g.next())
```

If we look at the console log, we can see that the first 2 lines we logged returned ‘a’ in the `value` property, and ‘result’ in the `value` property in the first 2 `console.log` lines. Then the remaining one has `undefined` as the `value`. The first `console.log` has `done` set to `false`, while the rest have `done` set to `true`. This is because the `return` statement ended the generator function’s execution. Anything below it is unreachable like a regular `return` statement.

Asynchronous Generators
=======================

Generators can also be used for asynchronous code. To make a generator function for asynchronous code, we can create an object with a method denoted with the special symbol `Symbol.asyncIterator` function. For example, we can write the following code to loop through a range of numbers, separating each iteration by 1 second:

```javascript
const rangeGen = (from = 1, to = 5) => {
  return {
    from,
    to,
    [Symbol.asyncIterator]() {
      return {
        currentNum: this.from,
        lastNum: this.to,
        async next() {
          await new Promise(resolve => setTimeout(
            resolve, 1000));
          if (this.currentNum <= this.lastNum) {
            return {
              done: false,
              value: this.currentNum++
            };
          } else {
            return {
              done: true
            };
          }
        }
      };
    }
  };
}

(async () => {
  for await (let value of rangeGen()) {
    console.log(value);
  }
})()
```

Note that the value that promise resolves to are in the `return` statements. The `next` function should always returns a promise. We can iterate through the values generated by the generator by using the `for await...of` loop, which works for iterating through asynchronous code. This is very useful since can loop through asynchronous code as if was synchronous code, which couldn’t be done before we had asynchronous generators function and the `async` and `await` syntax. We return an object with the `done` and `value` properties as with synchronous generators.

We can shorten the code above by writing:

```javascript
async function* rangeGen(start = 1, end = 5) {
  for (let i = start; i <= end; i++) {
    await new Promise(resolve => setTimeout(resolve, 1000));
    yield i;
  }
}
(async () => {
  for await (let value of rangeGen(1, 10)) {
    console.log(value);
  }
})()
```

Note that we can use the `yield` operator with `async` and `await`. A promise is still returned at the end of `rangeGen`, but this is a much shorter way to do it. It does the exact same thing that the previous code did, but it’s much shorter and easier to read.

Generator functions are very useful for creating iterators that can be used with a `for...of` loop. The `yield` statement will get the next value that will be returned by the iterator from any source of your choice. This means that we can turn anything into an iterable object. Also, we can use it to iterate through tree structures by defining a class with a method denoted by the Symbol `Symbol.iterator`, which creates a generator function that gets the items in the next level with the `yield*` keyword, which gets an item from the generator function directly. Also, we have the `return` statement to return the last item in the generator function. For asynchronous code we have `AsyncIterators`, which we can define by using `async`, `await`, and `yield` as we did above to resolve promises sequentially.