3

I have a piece of code:

function * input(){
 let array = [];
 while(true) {
 array.push(yield array);
 }
}
var gen = input();
console.log(gen.next("A"))
console.log(gen.next("B"))
console.log(gen.next("C"))
console.log(gen.next("D"))

When you run it you will get the following output:

{ value: [], done: false }
{ value: [ 'B' ], done: false }
{ value: [ 'B', 'C' ], done: false }
{ value: [ 'B', 'C', 'D' ], done: false }

Why the first line of the result doesn't include A in the array? There is one explanation from this page at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*#Passing_arguments_into_Generators. The comment says

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

But from my testing it seems not correct. My testing code is:

function* logGenerator() {
 console.log("before yield in function");
 yield 1;
 console.log("filler 1");
 yield 2;
 console.log("filler 2");
 yield 3;
 console.log("filler 3");
}
var gen = logGenerator();
console.log(gen.next());
console.log("-----------------");
console.log(gen.next());
console.log("-----------------");
console.log(gen.next());
console.log("-----------------");
console.log(gen.next());

The result is:

before yield in function
{ value: 1, done: false }
-----------------
filler 1
{ value: 2, done: false }
-----------------
filler 2
{ value: 3, done: false }
-----------------
filler 3
{ value: undefined, done: true }

As you can see, the first next() not only executed the statements before the first yield, but also the first yield statement. So that theory can't explain my question. Can anyone help to point me the right direction? Thanks in advance.

asked Jan 17, 2017 at 18:12
2
  • 1
    The yield operator must be taking precedence. Commented Jan 17, 2017 at 18:14
  • 1
    That is interesting. running console.log(gen.next()); first gives the expected output Commented Jan 17, 2017 at 18:19

2 Answers 2

1

Consider the first generator you wrote rewritten this way.

function * input(){
 let array = [];
 while(true) {
 var thingToAdd = yield array;
 console.log(thingToAdd);
 array.push(thingToAdd);
 }
}
var gen = input();
console.log(gen.next("A"))
console.log(gen.next("B"))
console.log(gen.next("C"))
console.log(gen.next("D"))

Isn't it clearer to see why "A" never gets added to the array? The first execution of the generator stops at the first yield statement well before the array ever gets modified. By the time execution returns to the generator, the passed in value is "B". The same dynamic is occurring in your code here array.push(yield array); Inner expressions are evaluated first, thus the yield halts execution before the push is accessed.

I believe that if you want to the generator to respect the first value you pass in, you need to call .next() once without any parameters. Every example I've seen does this.

Also reading the "Send" section of this article illustrates your situation.

Note that this model works well for a question and answer type interaction since we cannot have an answer before a question is asked, and all subsequent next calls will pass in the answer to the previous question and retrieve the next one.

var q1 = gen.next();
console.log(q1);
var a = userInput();
var q2 = gen.next(a);
console.log(q2);
var a2 = userInput();
...
answered Jan 17, 2017 at 19:09
Sign up to request clarification or add additional context in comments.

Comments

0
function * foo () {
 var i = 0
 yield i
 i += 1
 yield i
 i += 1
 i += 2
 yield i
}
var gen = foo()
console.log(gen.next()) // 0
console.log(gen.next()) // 1
console.log(gen.next()) // 4

Notice that the var gen = foo() merely creates an instance of a generator. It's the first invocation of .next() that starts the execution of the generator. Generators execute until they reach a yield statement and return the value of that yield statement. At this point the generator is paused until another invocation of .next() is performed.

So, everything is working as intended in your examples. In the first example, the first yield statement is returning the empty array. On the next .next() the array is populated with the value passed in, and then that array is yielded. In code:

function * foo (param) {
 var array = []
 // do nothing with param
 yield array
 array.push(param) // 'B'
 yield array
 array.push(param) // 'C'
 yield array
 array.push(param) // 'D'
 yield array
}

This matches the documentation:

If an optional value is passed to the generator's next() method, that value becomes the value returned by the generator's current yield operation.

answered Jan 17, 2017 at 19:16

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.