Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ES6 Generators: First call to next()

I was trying to understand how to use ES6 Generator functions. It seems pretty straight forward except for this one concept about making an empty next() function call while passing arguments. Here is the code I'm referring to from Mozilla docs.

function* logGenerator() {
  console.log(yield);
  console.log(yield);
  console.log(yield);
}

var gen = logGenerator();

// the first call of next executes from the start of the function
// until the first yield statement

gen.next();
gen.next('pretzel'); // pretzel
gen.next('california'); // california
gen.next('mayonnaise'); // mayonnaise

From what I understand, the code is executed only until the 1st yield statement so nothing is returned and then for the 2nd time we call next(), the code is executed until the 2nd yield which includes the 1st yield line, hence pretzel is logged to console.

If this is the case, in the code mentioned below how is 0 getting logged in the 1st call to next() ? I'm missing something here :(

function* idMaker() {
  var index = 0;
  while (index < 3)
    yield index++;
}

var gen = idMaker();

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

Reference : Mozilla Documentation

like image 920
Satya Sampathirao Avatar asked Sep 27 '16 19:09

Satya Sampathirao


Video Answer


2 Answers

Well, generator functions are kind of special. They can accept and return values multiple times throughout its execution, while 'normal' functions can only accept a fixed set of parameters, and return a single value. In your first example they are used to pass data to the generator (passing parameters multiple times), while in the second example its the other way around (returning values a several times).

Consider this example:

function* foo() {
    console.log("before a");

    var a = (yield 123);

    console.log("after a");

    yield (a * 2);

    console.log("end of function");
}

var bar = foo();

var x = bar.next(); // Runs until the first yield, prints 'before a'.
console.log(x.value); // 123
var y = bar.next(4); // Runs until the second yield, so prints 'after a'. 
console.log(y.value); // 8
var z = bar.next(); // prints 'end of function'.
console.log(z.done); // true

We pass in no data on the first next() call, letting it run until the first yield statement. Because of yield 123 the result of the call (x.value) is 123. The next time we call next() using 4 as parameter value we fill in local variable a inside the generator function. Code is executed until the next yield statement and returns the result of 8.

For the explanation why 0 instead of 1, see the other answer.

like image 164
Caramiriel Avatar answered Nov 03 '22 01:11

Caramiriel


I think what you're running into is the behavior of the post-increment operator, ie index++. If you changed it to the pre-increment operator, ie ++index, it will behave the way you're expecting.

The difference is that post-increment will add 1 to index after the expression is evaluated. Pre-increment will add 1 to index before the expression is evaluated, which I think is your goal.

like image 42
mwcz Avatar answered Nov 03 '22 01:11

mwcz