【问题标题】:Can I yield from an inner function?我可以从内部函数中屈服吗?
【发布时间】:2015-06-01 13:03:08
【问题描述】:

使用 ES6 生成器,我看到这样的代码:

var trivialGenerator = function *(array) {
    var i,item;
    for(var i=0; i < array.length; i++){
        item = array[i];
        yield item;
    };
};

是否可以改写更像下面的代码?

var trivialGenerator = function *(array) {
    array.forEach(function *(item){
        yield item;
    });
};

我之所以这么问,是因为经典的 for 循环令人讨厌。

【问题讨论】:

  • 这没有意义。您只需重新生成输入数组。无论如何,答案是否定的。在您的情况下,尽管您可以使用 for..of 循环。
  • 我不认为这是可能的......经典的 for 循环 stmt 将是正确的选择
  • 经典的 for 循环绝不是令人厌恶的。事实上,正如您所看到的,部分原因是由于生成器,它正在卷土重来。

标签: javascript for-loop generator yield ecmascript-6


【解决方案1】:

不,你不能在内部函数中使用yield。但在你的情况下,你不需要它。您始终可以使用 for-of 循环而不是 forEach 方法。它看起来会更漂亮,你可以在里面使用continuebreakyield

var trivialGenerator = function *(array) {
    for (var item of array) {
        // some item manipulation
        yield item;
    }
}

如果您对其中的项目进行了一些操作,您可以使用for-of。否则你绝对不需要创建这个生成器,因为array 本身就有iterator interface

【讨论】:

    【解决方案2】:

    不,您不能从回调中产生(从技术上讲,它不是“内部函数”,这意味着其他东西)。显然没有办法用* 等价物调用forEach,或者,如果回调本身是一个生成器,则告诉forEachyield * 调用回调。

    另一种方法是写一个函数forEachGen,如下:

    function *forEachGen(array, fn) { for (var i of array) yield *fn(i); }
    

    基本上将 for 循环移动到 forEachGen。定义一个小样本生成器为

    function *yieldSelf(item) { yield item; }
    

    forEachGen 将用作

    yield *forEachGen(array, yieldSelf);
    

    这假设回调本身是一个生成器,正如您在示例中所暗示的那样。如果回调是 ROF(常规旧函数),例如

    function returnSelf(item) { return item; }
    

    那就是

    function *forEachGen(array, fn) { for (var i of array) yield fn(i); }
    

    用作

    yield *forEachGen(array, returnSelf);
    

    如果您不介意将其添加到数组原型中,那么

    Object.defineProperty(Array.prototype, 'forEachGen', { value :
        function *(fn) { for (i of this) yield fn(i); }
    });
    

    然后做

    yield *array.forEachGen(yieldSelf)
    

    您可能对http://fitzgen.github.io/wu.js/ 感兴趣,它为生成器定义了一个包装器,包装器上有forEach 等方法。

    async / await

    使用await,您应该能够执行以下操作。

    定义一个简单的回调,它只为自己返回一个承诺。

    async function returnSelf(item) { return await item; }
    

    forEachAsync 将输入数组映射到一个 Promise 数组,并使用 await * 为所有准备就绪的单个 Promise 创建并返回一个 Promise。

    async function forEachAsync(values, fn) {
      return await *values.map(returnSelf);
    }
    

    我们可以将结果视为常规承诺,并将其打印在 then 中:

    forEachAsync([1,2,3], returnSelf) .
      then(result => console.log(result);
    

    或者使用一个小的 IIFE 异步包装器来等待结果然后打印出来:

    (async function() { 
        console.log(await forEachAsync([1,2,3], returnSelf));
    })();
    

    测试使用

    babel-node --experimental test.js
    

    【讨论】:

    • 按照我的定义,我认为它不可迭代的,或者你的意思是不可枚举的,还是我错过了什么? enumerable 的默认值为 false
    • 我遇到了类似的问题,但我无法将 for 循环移动到 forEachGen,因为在我的情况下,它不是一个数组,而是我从中获取项目的流。到目前为止,我能想到的唯一解决方案是缓冲项目,然后使用生成器函数对其进行迭代。但这严重破坏了流的“无限本质”,并会降低吞吐量。你知道如何处理这样的事情吗?
    猜你喜欢
    • 2020-04-11
    • 2014-03-12
    • 1970-01-01
    • 2011-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-19
    • 1970-01-01
    相关资源
    最近更新 更多