Wednesday, August 8, 2018

Generator in JavaScript

A generator is a special type of function that can be entered and exited a number of times. You might hear people describe it as, “a function that can be paused.

Example

function * generatorForLoop(num) {
  for (let i = 0; i < num; i += 1) {
    yield console.log(i);
  }
}

const genForLoop = generatorForLoop(5);

genForLoop.next(); // first console.log - 0
genForLoop.next(); // 1
genForLoop.next(); // 2
genForLoop.next(); // 3
genForLoop.next(); // 4

Yield delegator
Yield with asterisk can delegate it’s work to another generator. This way you can chain as many generators as you want.

function * anotherGenerator(i) {
  yield i + 1;
  yield i + 2;
  yield i + 3;
}

function * generator(i) {
  yield* anotherGenerator(i);
}

var gen = generator(1);

gen.next().value; // 2
gen.next().value; // 3
gen.next().value; // 4

Yield returns a value only once, and the next time you call the same function it will move on to the next yield statement.

Also in generators we always get the object as output. It always has two properties value and done. And as you can expect, value - returned value, and done shows us whether the generator has finished its job or not.


function * generator() {
  yield 5;
}

const gen = generator();

gen.next(); // {value: 5, done: false}
gen.next(); // {value: undefined, done: true}
gen.next(); // {value: undefined, done: true} - all other calls will produce the same result

Not only can yield be used in generators, return will also return the same object to you, but after you reach the first return statement the generator will finish it’s job.

function * generator() {
  yield 1;
  return 2;
  yield 3; // we will never reach this yield
}

const gen = generator();

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

Methods and initialization
Generators are reusable, but to be so — you need to initialize them, fortunately it is quite simple.

function * generator(arg = 'Nothing') {
  yield arg;
}

const gen0 = generator(); // OK
const gen1 = generator('Hello'); // OK
const gen2 = new generator(); // Not OK

generator().next(); // It will work, but every time from the beginning.

Method return():
function * generator() {
  yield 1;
  yield 2;
  yield 3;
}

const gen = generator();

gen.return(); // {value: undefined, done: true}
gen.return('Heeyyaa'); // {value: "Heeyyaa", done: true}

gen.next(); // {value: undefined, done: true} - all next() calls after return() will return the same output.

Return() will ignore any code in the generator function that you have. But will set the value based on a passed argument and set done to be true. Any calls next() after return() will return done-object.

Method throw():

function * generator() {
  yield 1;
  yield 2;
  yield 3;
}

const gen = generator();

gen.throw('Something bad'); // Error Uncaught Something bad
gen.next(); // {value: undefined, done: true}

It’s easy one all is throw() do — just throws the error. We can handle it using try — catch.

Not only next() we can use to iterate generator. But using for-of loop we get all the values (not the object) of our generator.

function * generator(arr) {
  for (const el in arr)
    yield el;
}

const gen = generator([0, 1, 2]);

for (const g of gen) {
  console.log(g); // 0 -> 1 -> 2
}

gen.next(); // {value: undefined, done: true}

This will not work with for-in loop and you can’t get access to properties by just typing number — generator[0] = undefined.

No comments:

Followers

Link