Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does the first next() in js generator function always execute until the first yield?

MDN suggested that "When the iterator's next() method is called, the generator function's body is executed until the first yield expression" and I do understand this example:

function* logGenerator() {
  console.log(0);
  console.log(1, yield);
  console.log(2, yield);
  console.log(3, yield);
}

var gen = logGenerator();

gen.next(); // 0
gen.next('pretzel'); // 1 pretzel
gen.next('california'); // 2 california
gen.next('mayonnaise'); // 3 mayonnaise

However, I am confused when I come across another example:

const foo = function*() {
  yield 10;
  yield 20;
};

const bar = foo();
console.log(bar.next()); // {value: 10, done: false}

If the first next() only execute code BEFORE the first yield, the value should be undefined. How does it pick up the value WITHIN the first yield? I am a bit confused here. Thanks for helping out.

like image 663
PhoenixPan Avatar asked Oct 16 '18 00:10

PhoenixPan


1 Answers

You can see what's going on better by logging what the generator yields each time, as well as adding values to the yield expressions so you can see what the generator is actually yielding (the MDN example didn't bother with this because it never uses the yielded values, it was just demonstrating the way the next() argument works).

Typically, you wouldn't use a single generator to yield values and also receive values through the next() argument, just one or the other. If it yields values, you would use yield <expression> as a statement by itself (similar to return), not generally inside an expression. If it receives values you use yield without a parameter inside an expression. But as you can see with my modified example, the underlying mechanism allows both at the same time. You just need to keep track of the confusing order of execution.

function* logGenerator() {
  console.log(0);
  console.log(1, yield 10);
  console.log(2, yield 20);
  console.log(3, yield 30);
}

var gen = logGenerator();

console.log("call 1 yielded", gen.next().value); // 0
console.log("call 2 yielded", gen.next('pretzel').value); // 1 pretzel
console.log("call 3 yielded", gen.next('california').value); // 2 california
console.log("call 4 yielded", gen.next('mayonnaise').value); // 3 mayonnaise

The first time you call the generator is executes console.log(0); and then starts executing console.log(1, yield 10). But when it gets to the yield expression, next() returns that value, before actually calling console.log().

The next time you call the generator, it resumes where it left off, which is constructing the arguments to console.log(). The yield 10 expression is replaced with the argument to next(), so it executes console.log(1, 'pretzel').

Then it starts executing console.log(2, yield 20), and the same thing happens -- it yields 20 before calling console.log().

What MDN was trying to show is that the argument to next() is only meaningful starting on the second call, because it replaces the value of yield in the interrupted expression when the generator resumes. Using an argument on the first call has no effect, because there's no interrupted expression to replace.

like image 170
Barmar Avatar answered Oct 10 '22 04:10

Barmar