Learnitweb

generator and generator function in JavaScript

1. Introduction

Regular functions return a single value which could be primitive or object. A regular function can not be used to return multiple values based on demand. Generators can do that. A Generator can be used to create a data stream. A Generator will return a value based on a certain logic, each time it is called. For example, if you want to get a number 1 to 100 from a function, starting with 1 and each time incremented by 1, you can use Generator in this case. A regular function can’t do it as it does not have any functionality to keep track of previously returned value.

A generator complies to both iterable and iterator protocols. A Generator object is returned by a generator function. So, let us now discuss about generator function.

2. Generator function

A generator function starts with function* (function keyword followed by asterisk) and returns a Generator object. A Generator is a function which can be re-executed with their variable bindings saved for re-execution.

Following are some important points about a generator function:

  • A Generator has a method next() which returns a value yielded by the yield expression defined in the generator function. The body of a generator function is not executed immediately, instead an iterator object for function is returned. When the iterator’s next() method is called, the generator function’s body is executed.
  • The return type of next() is an object with two properties, value and done. Here value is the value returned by the generator and done is a boolean which represents whether the generator has yielded the last value.
  • The return statement when executed makes the generator as finished. If a value is returned by the generator, its done property is set to true.
  • Calling next() on a finished iterator does not execute its body and returns an object of the form {value: undefined, done: true}.
function* generator(i) {
  yield i;
  yield i + 1;
  yield i + 2;
}
const gen = generator(15);
console.log(gen.next().value); //15
console.log(gen.next().value); //16
console.log(gen.next().value); //17

You can also use function expression syntax for Generator.

let myGenerator = function* generator(i) {
  yield i;
  yield i + 1;
  yield i + 2;
};
const gen = myGenerator(15);
console.log(gen.next().value); //15
console.log(gen.next().value); //16
console.log(gen.next().value); //17

3. yield*

yield* is used to call another generator.

function* double(x) {
  yield x * 2;
  yield x * 2;
}

function* generator(x) {
  yield* double(x); // call another generator double
}

let gen = generator(5);
console.log(gen.next().value); // 10
console.log(gen.next().value); // 10

4. Infinite generator

function* infiniteGenerator() {
  let i = 0;

  while (true) {
    yield i++;
  }
}

const generator = infiniteGenerator();

console.log(generator.next().value); // 0
console.log(generator.next().value); // 1
console.log(generator.next().value); // 2

5. Return statement in generator

function* generator() {
  yield 1;
  return 2;
  yield "unreachable";
}

var gen = generator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: true }
console.log(gen.next()); // { value: undefined, done: true }

6. Generators are not constructable

function* generator() {}
let obj = new generator(); // TypeError: generator is not a constructor

7. Generator as an object property

const generatorObj = {
  *generator() {
    yield 1;
    yield 2;
  },
};

const gen = generatorObj.generator();

console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }

8. Generator as an object method

class Generator {
  *generatorFn() {
    yield 1;
    yield 2;
    yield 3;
  }
}

const genObj = new Generator();
const gen = genObj.generatorFn();

console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }