使用 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;
});
};
不,你不能从回调中产生(从技术上讲,它不是“内部函数”,这意味着其他东西)。显然没有办法用
forEach
的等价物调用 *
,或者,如果回调本身是一个生成器,告诉 forEach
用 yield *
调用回调。
一个替代方案是写一个函数
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
将输入数组映射到一个承诺数组,并使用 await *
为所有准备就绪的个人承诺创建和返回承诺。
async function forEachAsync(values, fn) {
return await *values.map(returnSelf);
}
我们可以把结果当做一个常规的promise,打印出来
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);
});
}
}
}
使用这种方法,我可以将我的工作实例转换为一个迭代器,我可以循环使用它:
for await (const evt of createListFilesIterator(worker)) {
...
当然,我可以通过返回一个带有 onmessage 事件监听器的承诺来使它变得更简单,但我只是想看看这是否可行/有意义。我认为当你超越两个返回类型时,生成器会变得比事件监听器更简洁、更易于使用。但这肯定是我的意见。