3

I have a simple generator function

function *generate(arg) {
  console.log(arg)
  for(let i = 0; i < 3;i++) {
    console.log(yield i);
  }
}

Then I init the generator and trying to print values in console:

var gen = generate('arg'); //doesn't print
gen.next('a'); // prints 'arg'
gen.next('b'); // prints 'b'
gen.next('c'); // prints 'c'
// ... nothing surprising later

Where did argument a from first next() call go? Is there a way to utilize it inside generator function?

Here is a Babel REPL where you can see that result.

just-boris
  • 9,468
  • 5
  • 48
  • 84
  • 1
    The first `next` starts the generator’s execution. You can’t use the value you pass to it; it’s thrown out. You also weren’t allowed to pass anything but `undefined` to make this clear, but that’s probably changed since you’re not getting a TypeError: http://wiki.ecmascript.org/doku.php?id=harmony:generators (see If state = “newborn” under Internal method: send). – Ry- Sep 07 '15 at 22:37
  • Well, if in a draft version it was designed to throw an error, it makes me sure, that this argument should not be accessible to generator. Thanks for the comment! – just-boris Sep 07 '15 at 23:15
  • @minitech: I believe they are discussing a way of how to access the argument of the first call in ES7. – Felix Kling Sep 08 '15 at 02:41

2 Answers2

3

The next method is defined as follows:

25.3.1.2 Generator.prototype.next ( value )

The next method performs the following steps:

  1. Let g be the this value.
  2. Return GeneratorResume(g, value).

The GeneratorResume abstract operation uses value at step 10:

25.3.3.3 GeneratorResume ( generator, value )

The abstract operation GeneratorResume with arguments generator and value performs the following steps:

  1. Let genContext be the value of generator’s [[GeneratorContext]] internal slot.

  1. Resume the suspended evaluation of genContext using NormalCompletion(value) as the result of the operation that suspended it. Let result be the value returned by the resumed computation.

The first possibility is that the evaluations was suspended by the use of yield (i.e. "suspendedYield" state).

Its behavior is explained in 14.4.14 Runtime Semantics: Evaluation:

YieldExpression : yield

  1. Return GeneratorYield(CreateIterResultObject(undefined, false)).

(Analogous for YieldExpression : yield AssignmentExpression)

The GeneratorYield abstract operation suspends the generator as follows:

  1. Set the code evaluation state of genContext such that when evaluation is resumed with a Completion resumptionValue the following steps will be performed:

    1. Return resumptionValue.
    2. NOTE: This returns to the evaluation of the YieldExpression production that originally called this abstract operation.

So the value passed as the argument of the second next is used as the returned value of the first yield expression. And the value passed as the argument of the 3rd next is used as the returned value of the 2nd yield expression. And so on.

However, there is also the possibility that the generator hadn't started yet (i.e. "suspendedStart" state).

This is done by the GeneratorStart abstract operation:

  1. Set the code evaluation state of genContext such that when evaluation is resumed for that execution context the following steps will be performed:

But those "following steps" don't use the resumption value.

Therefore, the value passed as the argument of the first next is discarded.

Oriol
  • 274,082
  • 63
  • 437
  • 513
  • However, there's a proposal to add a new meta property to make it available. – Bergi Sep 08 '15 at 13:42
  • @Bergi Interesting. Can you link that proposal? – Oriol Sep 08 '15 at 17:56
  • 2
    I guess it was [this one](https://github.com/allenwb/ESideas/blob/master/Generator%20metaproperty.md). There was also an accompanying thread on es-discuss but I can't find it right now – Bergi Sep 08 '15 at 18:09
2

Nope, you can't use that first value. It's instructive if you change your code to :

'use strict';
function *generate(arg) {
  console.log(arg)
  for(let i = 0; i < 3;i++) {
    console.log(yield i);
  }
  return 'done';
}

var gen = generate('arg');
console.log(gen.next('a'));
console.log(gen.next('b'));
console.log(gen.next('c'));
console.log(gen.next('d'));

When you instantiate the generator, it does not start executing yet and nothing is logged. On the first gen.next('a'), you run up to the first yield which goes through the console.log(arg) in the generator and then does yield 0. This then gets console.log in the caller and you get {value: 0, done:false}, etc. until you complete the iteration. The overall output looks as follows:

arg
{ value: 0, done: false }
b
{ value: 1, done: false }
c
{ value: 2, done: false }
d
{ value: 'done', done: true }

The final done is the return value and will be undefined if you omit the return on the generator.

caasjj
  • 1,354
  • 8
  • 11
  • Actually this is not a question about what's going after `next`. The question was about first argument and where it is used. @minitech♦ gave the correct answer – nowhere, it was designed to dissappear – just-boris Sep 07 '15 at 23:19