ES6生成器机制-传递给next()的第一个值在哪里?

Neu*_*onQ 4 javascript yield generator

将参数传递给next()ES6生成器时,为什么忽略第一个值?更具体地讲,为什么这个输出x = 44而不是x = 43

function* foo() {
    let i = 0;
    var x = 1 + (yield "foo" + (++i));
    console.log(`x = ${x}`);
}

fooer = foo();

console.log(fooer.next(42));
console.log(fooer.next(43));

// output:
// { value: 'foo1', done: false }
// x = 44
// { value: undefined, done: true }
Run Code Online (Sandbox Code Playgroud)

我对这种生成器的行为的心理模型是这样的:

  1. 返回foo1并在yield处暂停(next返回的调用foo1将作为参数42
  2. 暂停直到下一次通话 next
  3. 在下一个收益率上继续行,var x = 1 + 42因为这是先前收到的参数
  4. 打印 x = 43
  5. 只需{done: true}从最后一个返回a next,而忽略其参数(43)并停止。

现在,显然,这不是正在发生的事情。所以... 我在这里怎么了?

Neu*_*onQ 5

我最终编写了这种代码来更彻底地调查行为(在重新阅读了有关generatorsMDN文档之后):

function* bar() {
    pp('in bar');
    console.log(`1. ${yield 100}`);
    console.log(`after 1`);
    console.log(`2. ${yield 200}`);
    console.log(`after 2`);
}
let barer = bar();
pp(`1. next:`, barer.next(1));
pp(`--- done with 1 next(1)\n`);
pp(`2. next:`, barer.next(2));
pp(`--- done with 2 next(2)\n`);
pp(`3. next:`, barer.next(3));
pp(`--- done with 3 next(3)\n`);
Run Code Online (Sandbox Code Playgroud)

输出以下内容:

in bar
1. next: { value: 100, done: false }
--- done with 1 next(1)

1. 2
after 1
2. next: { value: 200, done: false }
--- done with 2 next(2)

2. 3
after 2
3. next: { value: undefined, done: true }
--- done with 3 next(3)
Run Code Online (Sandbox Code Playgroud)

因此,显然正确的心理模型是这样的:

  • 在第一次调用next时,函数发生器体被运行到所述yield表达式中,“参数”的yield100第一次)被返回作为返回的值next,和所述发电机体暂停之前评估产量表达式的值 -在“之前”部分是关键

  • 仅在第二呼叫,以next在所述的值第一 yield计算/替换为给定的距离下在一个参数的值表达呼叫(不与在给定的一个一个如我所料),和执行运行,直到第二yield,并next返回第二个yield的参数的值- 这是我的错误:我假设第一个yield表达式的值是一次调用next的参数,但实际上是第二次调用next的参数,或者以另一种方式确切地说,它是在执行期间实际计算值的调用的参数next

这对谁发明了这个方法更有意义,因为对#的调用next次数是yield语句数量的一倍以上(还有最后一个返回{ value: undefined, done: true }信号终止),因此,如果不会忽略第一个调用的参数,则最后一个调用将不得不被忽略。同样,在评估next主体时,替换将从其先前调用的参数开始。这本来是更直观的恕直言,但我认为这也是遵循其他语言的生成器约定,而一致性最终是最好的……


没意思,但很有启发性:刚刚尝试在Python中做同样的探索,它显然实现了类似于Javascript的生成器,TypeError: can't send non-None value to a just-started generator当我尝试将参数传递给第一个调用时,我立即得到了一个提示next()(清楚地表明我的心智模型是错误的!) ,并且迭代器API也以抛出StopIteration异常结束,因此不需要“额外” next()就可以检查a是否done为真的(我想使用此额外的调用来利用副作用的最后一个下一个参数只会导致非常难以理解和调试代码...)。比在JS中更容易“摸索”。

  • 是的,这就是它的工作原理。从前一个 `next` 调用中获取参数是没有意义的,就好像它被缓冲了一样。它不允许立即“响应”传递的值。 (2认同)
  • 鉴于生成器实现来回传递“消息”(或传入和传出),人们总是需要从某个地方开始。我想也有可能比 `next` 调用多一个 `yield` 语句,因为第一个产生的值无处可去,但这在更常见的情况下是丑陋的。 (2认同)