Javascript 中的生成器函数如何工作?

the*_*lqd 3 javascript generator ecmascript-6

我从一篇文章中读到了这行代码,它们展示了函数生成器的工作流程。

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'));
Run Code Online (Sandbox Code Playgroud)

这是终端中的日志:

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 }
Run Code Online (Sandbox Code Playgroud)

但我找不到轻松理解这个流程的方法,这有点奇怪。如果有人对这个问题有简单的方法,请帮忙解释一下。

nem*_*035 7

基本

调用生成器函数会返回一个迭代器

调用.next()迭代器会返回一个以下形式的对象:

{ 
  value // current value of the iterator, 
  done  // boolean indicating if iteration is finished 
}
Run Code Online (Sandbox Code Playgroud)

调用.next()生成器提供的迭代器,从生成器中当前暂停点运行代码到下一个yield,在下一个暂停生成器yield,并推出迭代器方法yield返回的值,在.next()对象形式如上所示。

您传递给.next()此迭代器的任何内容都将从yield生成器中当前暂停的状态返回。

由于生成器不会yield在第一次调用a 时暂停.next(),因此传递到第一个.next()is 的任何内容都会被忽略。

如果没有yield剩余语句,则函数返回的任何内容都将是最后一个迭代器值。

此时,done标志将被设置为true,并且任何进一步的调用都.next()将返回相同的值。


运行示例

因此,就您的代码而言,这就是正在发生的事情。我将注释掉执行步骤后发生的每一行。

步骤1

f = foo();
Run Code Online (Sandbox Code Playgroud)

此时,迭代器已创建并存储在其中,f但生成器中的代码尚未实际运行。所以我们有:

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');
};
Run Code Online (Sandbox Code Playgroud)

第2步

f.next('a'); // returns { value: 'A', done: false }
Run Code Online (Sandbox Code Playgroud)

这会运行生成器中的代码直到第一个代码yield,并将生成的代码推出'A'调用.next()'a'传入的被.next()忽略,因为它是第一次调用(如上所述)。

注释掉运行的行留给我们:

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');
};
Run Code Online (Sandbox Code Playgroud)

步骤3

f.next('b'); // return { value: 'B', done: false }
Run Code Online (Sandbox Code Playgroud)

'b'从第一个yield(第一个PAUSE_POINT)返回并运行代码到下一个yield'B'从迭代器推出。

删除运行叶子的行:

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');
};
Run Code Online (Sandbox Code Playgroud)

步骤4

f.next('c'); // { value: undefined, done: true }
Run Code Online (Sandbox Code Playgroud)

'c'从暂停中传递出来yield,并且由于没有剩余的yields,运行到生成器的末尾并推出生成器返回的任何内容,在您的情况下这只是一个隐式return undefined. 由于我们到达了生成器函数的末尾,因此该done标志被设置为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;
};
Run Code Online (Sandbox Code Playgroud)

步骤 5 及后续步骤

f.next('d'); // { value: undefined, done: true }
Run Code Online (Sandbox Code Playgroud)

.next()生成器完成后(即doneis )的任何调用true都只会返回最后一个值。传递'd'到这个方法已经没有任何意义了。


附加示例

{ 
  value // current value of the iterator, 
  done  // boolean indicating if iteration is finished 
}
Run Code Online (Sandbox Code Playgroud)