在ES6生成器函数中使用return

30

我在寻找如果使用return语句而不是yield会发生什么的答案。

function *gen(){

 const val = yield someAsyncFn();
 assert.equal(val,4);
 return val;

}

返回语句(return)与生成器函数(yield)在行为上有何不同?我认为返回语句的行为像普通的返回语句一样,但在生成器函数中,它是否也调用了gen.return()呢?感觉有点混乱。

也许上面的问题与这个问题完全相同?

   function *gen(){

     const val = yield someAsyncFn();
     assert.equal(val,4);
     yield val;

   }
2个回答

35

return语句为迭代器的最后一个迭代(当done等于true时)提供返回值。

由于异步操作似乎与问题无关,因此我对您的示例进行了简化:

function *gen(){
 const val = yield 4;
 return val * 2;
}

var it = gen();
var val = it.next(); // { value: 4, done: false }
console.log(val.value); // 4
var res = it.next(val.value); // { value: 8, done: true }
console.log(res.value); // 8

如果没有 return 值,最后一次循环将返回一个 undefined 值:

function *gen2(){
 const val = yield 4;
 yield val * 2;
}

var it2 = gen2();
var val2 = it2.next(); // { value: 4, done: false }
console.log(val2.value); // 4
var res2 = it2.next(val2.value); // { value: 8, done: false }
console.log(res2.value); // 8
it2.next(); // { value: undefined, done: true }
请注意:一般来说,next 方法的调用次数总是比 yield 语句的数量多一个,这也是为什么第二个示例中会多调用一次 next 方法。

假设你正在使用像co这样的生成器运行器,那么当生成器完成时,你得到的值将是你所return的值:

co(function* () {
  var result = yield Promise.resolve(true);
  return result;
}).then(function (value) {
  console.log(value); // value equals result
}, function (err) {
  console.error(err.stack);  // err equals result
});

重要提示:如果您正在使用for ... of循环或类似Array.from的方法来遍历迭代器,那么return语句将会被忽略(由于您正在进行异步操作,这可能无关紧要):

function *gen(){
 const val = yield 4;
 return val * 2;
}

for (let value of gen()) {
  console.log(value);
}
// 4
最终,调用生成器只会创建一个迭代器。最后一个迭代器返回的值是否相关,完全取决于您如何使用它。

如果我想从生成器函数中返回除“undefined”之外的内容,那么我必须使用“return”而不是“yield”吗?如果在yield语句之前有一个return语句会发生什么?假设yield语句永远不会被调用。 - Alexander Mills
2
这真的取决于你如何运行你的生成器。如果你有一个带有承诺的异步流程(使用co或类似的东西),那么一旦你的生成器完成运行,解析值将是你的return的值。如果你手动使用next运行它,你可以自由决定你对最后一次迭代的值做什么。如果你使用循环来运行它,return值将被忽略。 - nils
2
一个 return 语句总是终止函数调用,即使在生成器中也是如此。因此,在此之后不会执行任何其他语句(包括 yield)。 - nils
4
非常有用的信息,尤其是关于 for ... of 循环的最后一条提示。谢谢! - Slavik Meltser

7
除了 @nils 给出的详细答案,还有一种额外的方法可以捕获生成器函数的返回值,即将其作为 yield* 的值(必须在另一个生成器函数内部):
  function* arrayGenerator(arr) {
    for (const element of arr) 
      yield element
    return arr.length
  }

  function* elementsFollowedByLength(arr) {
    const len = yield* arrayGenerator(arr);
    yield len;
  }


请注意第一个生成器函数在产生数组元素后返回一个值。
通过yield*,第二个生成器函数能够使第一个生成器函数产生其所有的值。当第一个生成器函数结束并返回时,该返回值成为yield*表达式的值。

所以这段内容不适合放在评论区,也不是答案,但它似乎很相关:与其创建另一个生成器,手动迭代结果迭代器可以获取return值。所以:while (true) { const { value, done } = await result.next(); console.log(value); if (done) break; } ...你也可以使用if (done)语句和else子句,如果最终的return值具有不同类型或需要不同的操作。 - mindplay.dk

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