ES6生成器机制 - 传递给next()的第一个值在哪里? [英] ES6 generators mechanism - first value passed to next() goes where?

查看:213
本文介绍了ES6生成器机制 - 传递给next()的第一个值在哪里?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

When passing parameters to next() of ES6 generators, why is the first value ignored? More concretely, why does the output of this say x = 44 instead of 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 }

我对这种生成器行为的心理模型如下:

My mental model for the behavior of such a generator was something like:


  1. 返回 foo1 并暂停收益(以及 next 调用哪个返回 foo1 作为参数 42

  2. 暂停,直到下一次调用下一步

  3. 下一次收益,继续行 var x = 1 + 42 因为这是以前收到的参数

  4. print x = 43

  5. 从最后一个 next 返回 {done:true} ,忽略其参数( 43 )并停止。

  1. return foo1 and pause at yield (and the next call which returns foo1 takes as argument 42)
  2. pause until next call to next
  3. on next yield proceed to the line with var x = 1 + 42 because this was the argument previously received
  4. print x = 43
  5. just return a {done: true} from the last next, ignoring its argument (43) and stop.

现在,显然,这不是正在发生的事情。那么...... 我在这里弄错了什么?

Now, obviously, this is not what's happening. So... what am I getting wrong here?

推荐答案

我最后写的这种用于更彻底地调查行为的代码(在重新阅读...之后 - 阅读关于生成器的MDN文档):

I ended up writing this kind of code to investigate the behavior more thoroughly (after re-re-...-reading the MDN docs on generators):

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`);

输出:

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)

所以显然正确的心理模型是这样的:

So apparently the correct mental model would be like this:


  • 首次调用 next 时,生成器函数体运行到 yield 表达式,参数为 yield (第一次 100 )作为 next ,生成器主体在评估yield表达式的值之前暂停 - 之前部分是至关重要的

  • on first call to next, the generator function body is run up to the yield expression, the "argument" of yield (100 the first time) is returned as the value returned by next, and the generator body is paused before evaluating the value of the yield expression -- the "before" part is crucial

仅在第二次调用 next 时才是第一个的值产量表达式计算/替换为调用中给出的参数的值(不是我预期的 previous 中给出的那个),并且执行运行直到第二个收益率下一个返回第二个收益率的参数值 - 这是我的错误:我假设第一个 yield 表达式的值是第一次调用 next ,但它实际上是第二次调用 next 的参数,或者,另一种方式说来,它是 next 调用的参数,在执行过程中实际计算了值

only on the second call to next is the value of the first yield expression computed/replaced with the value of the argument given to next on this call (not with the one given in the previous one as I expected), and execution runs until the second yield, and next returns the value of the argument of this second yield -- here was my mistake: I assumed the value of the first yield expression is the argument of the first call to next, but it's actually the argument of the second call to next, or, another way to put it, it's the argument of the call to next during whose execution the value is actually computed

这可能对谁发明这个更有意义,因为 next 的调用次数是一次 yield 语句的数量(还有最后一个返回 {value:undefined,done:true} 到sig nal termination),所以如果第一次调用的参数不会被忽略,那么最后一次调用之一就必须被忽略。此外,在评估next的主体时,替换将从其之前的调用的参数开始。这个会更直观imho ,但我认为这也是关于其他语言中的生成器的约定,并且一致性最终是最好的...

This probably made more sense to who invented this because the # of calls to next is one more times the number of yield statements (there's also the last one returning { value: undefined, done: true } to signal termination), so if the argument of the first call would not have been ignored, then the one of the last call would have had to be ignored. Also, while evaluating the body of next, the substitution would have started with the argument of its previous invocation. This would have been much more intuitive imho, but I assume it's about following the convention for generators in other languages too and consistency is the best thing in the end...

偏离主题但很有启发性:刚尝试在Python中进行相同的探索,这显然实现了类似于Javascript的生成器,我立即得到 TypeError:当尝试将参数传递给第一次调用时,无法将非None值发送到刚刚启动的生成器 ()(明确表示我的心智模型错误!),迭代器API也以抛出 StopIteration 异常结束,所以没有额外的 next()只需检查完成是否为真(我想这个额外的调用是为了副作用利用最后一个参数只会导致非常难以理解和调试代码......)。比在JS中更容易理解它......

Off-topic but enlightening: Just tried to do the same exploration in Python, which apparently implements generators similar to Javascript, I immediately got a TypeError: can't send non-None value to a just-started generator when trying to pass an argument to the first call to next() (clear signal that my mental model was wrong!), and the iterator API also ends by throwing a StopIteration exception, so no "extra" next() needed just to check if the done is true (I imagine using this extra call for side effects that utilize the last next argument would only result in very hard to understand and debug code...). Much easier to "grok it" than in JS...

这篇关于ES6生成器机制 - 传递给next()的第一个值在哪里?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆