What Are Generators?

A generator is a function that returns an iterator. Generator functions are indicated by a star character (*) after the function keyword and use the new yield keyword. It doesn’t matter if the star is directly next to function or if there’s some whitespace between it and the * character, as in this example:

  1. // generator
  2. function *createIterator() {
  3. yield 1;
  4. yield 2;
  5. yield 3;
  6. }
  7. // generators are called like regular functions but return an iterator
  8. let iterator = createIterator();
  9. console.log(iterator.next().value); // 1
  10. console.log(iterator.next().value); // 2
  11. console.log(iterator.next().value); // 3

The * before createIterator() makes this function a generator. The yield keyword, also new to ECMAScript 6, specifies values the resulting iterator should return when next() is called, in the order they should be returned. The iterator generated in this example has three different values to return on successive calls to the next() method: first 1, then 2, and finally 3. A generator gets called like any other function, as shown when iterator is created.

Perhaps the most interesting aspect of generator functions is that they stop execution after each yield statement. For instance, after yield 1 executes in this code, the function doesn’t execute anything else until the iterator’s next() method is called. At that point, yield 2 executes. This ability to stop execution in the middle of a function is extremely powerful and leads to some interesting uses of generator functions (discussed in the “Advanced Iterator Functionality” section).

The yield keyword can be used with any value or expression, so you can write generator functions that add items to iterators without just listing the items one by one. For example, here’s one way you could use yield inside a for loop:

  1. function *createIterator(items) {
  2. for (let i = 0; i < items.length; i++) {
  3. yield items[i];
  4. }
  5. }
  6. let iterator = createIterator([1, 2, 3]);
  7. console.log(iterator.next()); // "{ value: 1, done: false }"
  8. console.log(iterator.next()); // "{ value: 2, done: false }"
  9. console.log(iterator.next()); // "{ value: 3, done: false }"
  10. console.log(iterator.next()); // "{ value: undefined, done: true }"
  11. // for all further calls
  12. console.log(iterator.next()); // "{ value: undefined, done: true }"

This example passes an array called items to the createIterator() generator function. Inside the function, a for loop yields the elements from the array into the iterator as the loop progresses. Each time yield is encountered, the loop stops, and each time next() is called on iterator, the loop picks up with the next yield statement.

Generator functions are an important feature of ECMAScript 6, and since they are just functions, they can be used in all the same places. The rest of this section focuses on other useful ways to write generators.

W> The yield keyword can only be used inside of generators. Use of yield anywhere else is a syntax error, including functions that are inside of generators, such as: W> W> js W> function *createIterator(items) { W> W> items.forEach(function(item) { W> W> // syntax error W> yield item + 1; W> }); W> } W> W> W> Even though yield is technically inside of createIterator(), this code is a syntax error because yield cannot cross function boundaries. In this way, yield is similar to return, in that a nested function cannot return a value for its containing function.

Generator Function Expressions

You can use function expressions to create generators by just including a star (*) character between the function keyword and the opening parenthesis. For example:

  1. let createIterator = function *(items) {
  2. for (let i = 0; i < items.length; i++) {
  3. yield items[i];
  4. }
  5. };
  6. let iterator = createIterator([1, 2, 3]);
  7. console.log(iterator.next()); // "{ value: 1, done: false }"
  8. console.log(iterator.next()); // "{ value: 2, done: false }"
  9. console.log(iterator.next()); // "{ value: 3, done: false }"
  10. console.log(iterator.next()); // "{ value: undefined, done: true }"
  11. // for all further calls
  12. console.log(iterator.next()); // "{ value: undefined, done: true }"

In this code, createIterator() is a generator function expression instead of a function declaration. The asterisk goes between the function keyword and the opening parentheses because the function expression is anonymous. Otherwise, this example is the same as the previous version of the createIterator() function, which also used a for loop.

I> Creating an arrow function that is also a generator is not possible.

Generator Object Methods

Because generators are just functions, they can be added to objects, too. For example, you can make a generator in an ECMAScript 5-style object literal with a function expression:

  1. var o = {
  2. createIterator: function *(items) {
  3. for (let i = 0; i < items.length; i++) {
  4. yield items[i];
  5. }
  6. }
  7. };
  8. let iterator = o.createIterator([1, 2, 3]);

You can also use the ECMAScript 6 method shorthand by prepending the method name with a star (*):

  1. var o = {
  2. *createIterator(items) {
  3. for (let i = 0; i < items.length; i++) {
  4. yield items[i];
  5. }
  6. }
  7. };
  8. let iterator = o.createIterator([1, 2, 3]);

These examples are functionally equivalent to the example in the “Generator Function Expressions” section; they just use different syntax. In the shorthand version, because the createIterator() method is defined with no function keyword, the star is placed immediately before the method name, though you can leave whitespace between the star and the method name.