JavaScript生成器的理解

3

我有一段代码:

function * input(){
    let array = [];
    while(true) {
        array.push(yield array);
    }
}

var gen = input();
console.log(gen.next("A"))
console.log(gen.next("B"))
console.log(gen.next("C"))
console.log(gen.next("D"))

当您运行它时,您将获得以下输出:
{ value: [], done: false }
{ value: [ 'B' ], done: false }
{ value: [ 'B', 'C' ], done: false }
{ value: [ 'B', 'C', 'D' ], done: false }

为什么结果的第一行不包括数组中的A? 有一个解释来自于这个页面:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*#Passing_arguments_into_Generators。评论中写到:

第一次调用next()从函数的开始一直执行到第一个yield语句。

但是从我的测试来看,这个解释似乎不正确。我的测试代码如下:

function* logGenerator() {
    console.log("before yield in function");
    yield 1;
    console.log("filler 1");
    yield 2;
    console.log("filler 2");
    yield 3;
    console.log("filler 3");
}

var gen = logGenerator();

console.log(gen.next());
console.log("-----------------");
console.log(gen.next());
console.log("-----------------");
console.log(gen.next());
console.log("-----------------");
console.log(gen.next());

结果是:
before yield in function
{ value: 1, done: false }
-----------------
filler 1
{ value: 2, done: false }
-----------------
filler 2
{ value: 3, done: false }
-----------------
filler 3
{ value: undefined, done: true }

正如你所看到的,第一个next()不仅执行了第一个yield之前的语句,还执行了第一个yield语句。因此,这个理论不能解释我的问题。有人能帮我指出正确的方向吗?提前致谢。

1
yield 操作符必须具有优先权。 - Travis J
1
这很有趣。首先运行 console.log(gen.next()); 将会得到预期的输出。 - D. Walsh
2个回答

1
考虑你写的第一个生成器,按照以下方式进行重写。

function * input(){
    let array = [];
    while(true) {
        var thingToAdd = yield array;
        console.log(thingToAdd);
        array.push(thingToAdd);
    }
}

var gen = input();
console.log(gen.next("A"))
console.log(gen.next("B"))
console.log(gen.next("C"))
console.log(gen.next("D"))

不难看出为什么"A"从未添加到数组中,第一次执行生成器在修改数组之前就停止于第一个yield语句。当执行返回到生成器时,传入的值为"B"。你的代码中也存在同样的动态,array.push(yield array);内部表达式先进行评估,因此yield在访问push之前停止执行。我认为,如果您想让生成器尊重传入的第一个值,需要在不带任何参数的情况下调用.next()一次。我见过的所有示例都是这样做的。此外,阅读this article的“发送”部分可以说明您的情况。请注意,该模型适用于问答类型的交互,因为在提问之前我们无法得到答案,并且所有后续的next调用都将传递上一个问题的答案并检索下一个问题。
var q1 = gen.next();
console.log(q1);
var a = userInput();
var q2 = gen.next(a);
console.log(q2);
var a2 = userInput();
...

0
function * foo () {
  var i = 0
  yield i

  i += 1
  yield i

  i += 1
  i += 2
  yield i
}

var gen = foo()
console.log(gen.next()) // 0
console.log(gen.next()) // 1
console.log(gen.next()) // 4

请注意,var gen = foo() 仅仅是创建了一个生成器的实例。第一次调用 .next() 才会开始执行生成器。生成器会一直执行,直到遇到一个 yield 语句,并返回该 yield 语句的值。此时,生成器会暂停,直到进行另一个 .next() 的调用。
因此,在您的示例中,一切都按预期工作。在第一个示例中,第一个 yield 语句返回空数组。在下一个 .next() 中,数组将被填充传入的值,然后该数组将被 yield。代码如下:
function * foo (param) {
  var array = []
  // do nothing with param
  yield array

  array.push(param) // 'B'
  yield array

  array.push(param) // 'C'
  yield array

  array.push(param) // 'D'
  yield array
}

这与文档相匹配:

如果将可选值传递给生成器的next()方法,则该值将成为生成器当前yield操作返回的值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接