我可以从内部函数中产生吗?

Can I yield from an inner function?

本文关键字:内部函数 我可以      更新时间:2023-09-26

使用 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;
    });
};

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

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

如果您对其中的项目进行了一些操作,则可以使用 for-of。否则,您绝对不需要创建此生成器,因为array本机具有iterator接口。

不,你不能从回调中产生(从技术上讲,它不是一个"内部函数",这意味着别的东西(。显然没有办法用等效的 * 调用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);

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

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将输入数组映射到一个承诺数组,并使用await *为所有准备就绪的单个承诺创建和返回承诺。

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
你不能,

但你可以创建自己的包装器来将函数转换为生成器。这是我是如何做到的:

export async function* createListFilesIterator(
  worker: DriveWorker,
): AsyncGenerator<FilesResp, void, unknown> {
  const messages: FilesResp[] = [];
  let processed = 0;
  let waited = 0;
  let done = false;
  worker.onmessage = (msg) => {
    const { data: evt } = msg;
    if (evt.type === "files") {
      messages.push(evt);
    } else if (evt.type === "done") {
      done = true;
    }
  };
  while (processed < messages.length || (!done && waited <= 16)) {
    if (processed < messages.length) {
      yield messages[processed];
      waited = 0;
      processed += 1;
    } else {
      waited += 1;
      await new Promise((resolve) => {
        setTimeout(resolve, 1000 * waited * 0.5);
      });
    }
  }
}

使用此方法,我可以将我的 worker 实例转换为迭代器,我可以循环使用:

for await (const evt of createListFilesIterator(worker)) {
   ...

当然,我可以通过返回一个带有onmessage事件侦听器的承诺来使它更简单,但我只是想看看这是否可行/有意义。我认为当您超过两种返回类型时,生成器会变得比事件侦听器更干净、更易于使用。但这是我的意见,当然。