Reputation: 157
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
Upvotes: 5
Views: 2221
Reputation: 7277
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.
Upvotes: 10
Reputation: 2271
From what I understand, the code is executed only until the 1st yield statement so nothing is returned
In the Mozilla example the "yields" all return SOMETHING, they return undefined. It's like having "return" with nothing after it, which also "returns" undefined.
What is special here is that since this is a generator-function the returns / yields happen in the MIDDLE of the log() -statement. They happen as soon as the "yield" -statement STARTS EXECUTING. That means they "jump out" of the current function BEFORE the log() statement has finished, so nothing gets to the console on the first call. In other words the 1st yield causes the function* to "jump out" before the log-statement gets its chance to execute.
When you call the 2nd next() the execution continues exactly from the point where it stopped last time but with the value of the term 'yield' now being what was passed as argument to the next(). Therefore the log-statement now continues and prints that value.
The tricky part of yield to get is this "instantaneous" freeze and on next next() continuation from exactly the same point where it froze last time BUT with the term 'yield' now having the value that was passed as argument to next(). If yield occurs within some statement like log(yield) then the freeze happens BEFORE the containing statement is able to execute.
Or in other words I think tricky part (and it is tricky that's why I'm repeating myself) is that the yield-statement causes the function to return BEFORE the yield-statement has completed itself. It will be completed only on the next next(), by having the value of "yield" become whatever was the argument to that call to next().
The difference in your example is that your log() -statements all happen OUTSIDE the generator-function, in Mozilla example they happen inside it. Therefore your logs() get executed with the value they got from the call to next(). Since your logs() happen outside the generator, your none of your log()-statements get interrupted in the middle.
Upvotes: 1
Reputation: 9321
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.
Upvotes: 5