Skip to content

Generators

Another way to write the downToOne function is to use a Generator. To instantiate a Generator object, one must use the function * declaration. Generators are functions that can be exited and later re-entered with its context (variable bindings) saved across re-entrances.

For example, the downToOne function above can be rewritten as:

1
2
3
4
5
6
7
function * downToOne(n) {
  for (let i = n; i > 0; --i) {
    yield i;
  }
}

[...downToOne(5)] // [ 5, 4, 3, 2, 1 ]

Generators return an iterable object. When the iterator's next() function is called, it is executed until the first yield expression, which specifies the value to be returned from the iterator or with yield*, which delegates to another generator function. When a return expression is called in the generator, it will mark the generator as done and pass back as the return value. Further calls to next() will not return any new values.

Sample code

// Yield Example
function * idMaker() {
  var index = 0;
  while (index < 2) {
    yield index;
    index = index + 1;
  }
}

var gen = idMaker();

gen.next().value; // 0
gen.next().value; // 1
gen.next().value; // undefined

The yield* expression enables a generator to call another generator function during iteration.

// Yield * Example
function * genB(i) {
  yield i + 1;
  yield i + 2;
  yield i + 3;
}

function * genA(i) {
  yield i;
  yield* genB(i);
  yield i + 10;
}

var gen = genA(10);

gen.next().value; // 10
gen.next().value; // 11
gen.next().value; // 12
gen.next().value; // 13
gen.next().value; // 20
// Generator Return Example
function* yieldAndReturn() {
  yield "Y";
  return "R";
  yield "unreachable";
}

var gen = yieldAndReturn()
gen.next(); // { value: "Y", done: false }
gen.next(); // { value: "R", done: true }
gen.next(); // { value: undefined, done: true }

External resources