admin管理员组

文章数量:1391934

I read these line of codes from an article and they showed the work flow of function generator.

var foo, f;

foo = function* () {
  console.log('generator 1');
  console.log('yield 1', yield 'A');
  console.log('generator 2');
  console.log('yield 2', yield 'B');
  console.log('generator 3');
};

f = foo();

console.log('tick 1');
console.log(f.next('a'));
console.log('tick 2');
console.log(f.next('b'));
console.log('tick 3');
console.log(f.next('c'));
console.log('tick 4');
console.log(f.next('d'));

This is the log in the terminal:

tick 1
generator 1
{ value: 'A', done: false }
tick 2
yield 1 b
generator 2
{ value: 'B', done: false }
tick 3
yield 2 c
generator 3
{ value: undefined, done: true }
tick 4
{ value: undefined, done: true }

But I can not find the way to easily understand this flow, it is kind of weird. If someone has a simple approach to this problem, please help to explain.

I read these line of codes from an article and they showed the work flow of function generator.

var foo, f;

foo = function* () {
  console.log('generator 1');
  console.log('yield 1', yield 'A');
  console.log('generator 2');
  console.log('yield 2', yield 'B');
  console.log('generator 3');
};

f = foo();

console.log('tick 1');
console.log(f.next('a'));
console.log('tick 2');
console.log(f.next('b'));
console.log('tick 3');
console.log(f.next('c'));
console.log('tick 4');
console.log(f.next('d'));

This is the log in the terminal:

tick 1
generator 1
{ value: 'A', done: false }
tick 2
yield 1 b
generator 2
{ value: 'B', done: false }
tick 3
yield 2 c
generator 3
{ value: undefined, done: true }
tick 4
{ value: undefined, done: true }

But I can not find the way to easily understand this flow, it is kind of weird. If someone has a simple approach to this problem, please help to explain.

Share Improve this question edited Feb 27, 2018 at 17:02 nem035 35.5k6 gold badges92 silver badges104 bronze badges asked Sep 9, 2016 at 16:32 thelonglqdthelonglqd 1,87219 silver badges30 bronze badges 2
  • What do you find weird in particular? – Bergi Commented Sep 9, 2016 at 17:10
  • The order is perfectly correct and very clear after you learn about how your code forms an AST. – rishat Commented Sep 9, 2016 at 17:15
Add a ment  | 

2 Answers 2

Reset to default 7

Basics

Calling the generator function returns an iterator.

Calling .next() on an iterator returns an object in the form:

{ 
  value // current value of the iterator, 
  done  // boolean indicating if iteration is finished 
}

Calling .next() on the iterator provided by the generator, runs the code from the current paused point in the generator to the next yield, pauses the generator on this next yield and pushes out whatever is the yield value as the value returned from the .next() method of the iterator, in the object form shown above.

Anything that you pass into .next() of this iterator will be returned from the currently paused yield within the generator.

Since the generator isn't paused on a yield in the first call to .next(), anything passed into the first .next() is is just ignored.

If there are no yield statements left, whatever the function returns will be the last iterator value.

At this point the done flag will be set to true and any further calls to .next() will return the same value.


Running Example

So, in terms of your code, here's what is happening. I'll ment out each line that happens after an execution step.

Step 1

f = foo();

At this point, the iterator is created and stored in f but no code in the generator has actually ran. So we have:

function* () {
  console.log('generator 1');
  console.log('yield 1', yield 'A');
  console.log('generator 2');
  console.log('yield 2', yield 'B');
  console.log('generator 3');
};

Step 2

f.next('a'); // returns { value: 'A', done: false }

This runs the code within the generator up to the first yield and pushes the yielded 'A' out of the .next() call. The 'a' that is passed into .next() is just ignored, since its the first call (explained above).

Commenting out the lines that ran leaves us with:

function* () {
  // console.log('generator 1');
  console.log('yield 1', PAUSE_POINT); // we're paused on the `yield`. Essentially half of this line is done
  console.log('generator 2');
  console.log('yield 2', yield 'B');
  console.log('generator 3');
};

Step 3

f.next('b'); // return { value: 'B', done: false }

This returns 'b' from the first yield (the first PAUSE_POINT) and runs the code to the next yield, pushing 'B' out from the iterator.

Removing the lines that run leaves with:

function* () {
  // console.log('generator 1');
  // console.log('yield 1', 'b'); // this PAUSE_POINT returns 'b'
  // console.log('generator 2');
  console.log('yield 2', PAUSE_POINT); // no we're paused here
  console.log('generator 3');
};

Step 4

f.next('c'); // { value: undefined, done: true }

Which passes 'c' out of the paused yield and, since there are no more yields left, runs to the end of the generator and pushes out whatever the generator returns, which in your case is just an implicit return undefined. Since we reached the end of the generator function, the done flag is set to true.

function* () {
  // console.log('generator 1');
  // console.log('yield 1', 'b'); // this PAUSE_POINT returns 'b'
  // console.log('generator 2');
  // console.log('yield 2', 'c'); // this PAUSE_POINT returns 'c'
  // console.log('generator 3');
  // here we have an implicit return undefined;
};

Step 5 and further on

f.next('d'); // { value: undefined, done: true }

Any calls to .next() after the generator is finished (i.e. done is true) just returns whatever the last value was. Passing 'd' into this method serves no purpose anymore.


Additional Example

var foo = function* () {
  console.log('first call to next runs to yield #1');
  var a = yield 'A'; // this yield pushes 'A' and returns 'a'
  console.log('second call to next runs to yield #2');
  var b = yield 'B'; // this yield pushes 'B' and returns 'b'
  console.log('third call to next runs to the end of the generator ');
  // there's no return statement here so we are returning undefined
};

var f = foo();

console.log(f.next('this gets ignored')); // { value: 'A', done: false }
console.log(f.next('a'));                 // { value: 'B', done: false }
console.log(f.next('b'));                 // { value: undefined, done: true }

// any further call just returns whatever the last returned value was
console.log(f.next('this also gets ignored since we are done')); // { value: undefined, done: true }

Simple explain is that yield keyword stops executing code, yield is like breakpoint, next generator call executes code after last breakpoint to next breakpoint ( if any exists ). So generator function is called in parts, check ments in my example code.

var gen=function*(){
  
  //1.start of first call next()
  
  yield "A";//1.here first next() ends
  
  //2.start of second call next();
  
  yield "B";//2.here end second next();
  
  //3.start of third call next()
  
  
  yield "C";//3.end of third next() call
  
  //NEXT AFTER THIRD. here every next() after third starts
  
  //no code no yield so value is undefined
  
  //here every next() after third ends
  
  
}();

console.log(gen.next().value);//A
console.log(gen.next().value);//B
console.log(gen.next().value);//C
console.log(gen.next().value);//undefined

本文标签: ecmascript 6How does a generator function work in JavascriptStack Overflow