Tom*_*ica 6 javascript arrays asynchronous yield
尽管yield关键字的主要目的是提供某些数据的迭代器,但使用它来创建异步循环也相当方便:
function* bigLoop() {
// Some nested loops
for( ... ) {
for( ... ) {
// Yields current progress, eg. when parsing file
// or processing an image
yield percentCompleted;
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后可以异步调用:
function big_loop_async(delay) {
var iterator = big_loop();
function doNext() {
var next = iterator.next();
var percent_done = next.done?100:next.value;
console.log(percent_done, " % done.");
// start next iteration after delay, allowing other events to be processed
if(!next.done)
setTimeout(doNext, delay);
}
setTimeout(doNext, delay);
}
Run Code Online (Sandbox Code Playgroud)
然而,在现代 JavaScript 中,基于回调的循环已经变得相当流行。我们有Array.prototype.forEach,Array.prototype.find或Array.prototype.sort。所有这些都基于每次迭代传递的回调。我什至听说有人建议我们尽可能使用它们,因为它们可以比标准 for 循环更好地优化。
我还经常使用基于回调的循环来抽象出一些复杂的循环模式。
这里的问题是,是否有可能将它们变成yield基于迭代器?作为一个简单的示例,考虑我希望您对数组进行异步排序。
\n\n\ntl;dr:您可以\xe2\x80\x99t 执行此操作,但请查看您可以使用最新的 V8 和bluebird执行的其他操作执行的其他操作:
\n\nRun Code Online (Sandbox Code Playgroud)\nasync function asyncReduce() {\n const sum = await Promise.reduce(\n [1, 2, 3, 4, 5],\n async (m, n) => m + await Promise.delay(200, n),\n 0\n );\n\n console.log(sum);\n}\n
不,\xe2\x80\x99s 不可能Array.prototype.sort从其比较函数异步接受比较结果;你将不得不完全重新实现它。对于其他个别情况,可能存在黑客行为,例如协程forEach(它甚至不一定像您期望的那样工作,因为每个生成器都会运行到第一个生成器之前,然后yield从yield):
function syncForEach() {\n [1, 2, 3, 4, 5].forEach(function (x) {\n console.log(x);\n });\n}\nRun Code Online (Sandbox Code Playgroud)\n\n\n\nfunction delayed(x) {\n return new Promise(resolve => {\n setTimeout(() => resolve(x), Math.random() * 1000 | 0);\n });\n}\n\nfunction* chain(iterators) {\n for (const it of iterators) {\n yield* it;\n }\n}\n\nfunction* asyncForEach() {\n yield* chain(\n [1, 2, 3, 4, 5].map(function* (x) {\n console.log(yield delayed(x));\n })\n );\n}\nRun Code Online (Sandbox Code Playgroud)\n\n和reduce,本质上效果很好(直到您查看性能):
function syncReduce() {\n const sum = [1, 2, 3, 4, 5].reduce(function (m, n) {\n return m + n;\n }, 0);\n\n console.log(sum);\n}\nRun Code Online (Sandbox Code Playgroud)\n\n\n\nfunction* asyncReduce() {\n const sum = yield* [1, 2, 3, 4, 5].reduce(function* (m, n) {\n return (yield* m) + (yield delayed(n));\n }, function* () { return 0; }());\n\n console.log(sum);\n}\nRun Code Online (Sandbox Code Playgroud)\n\n但是,是的,没有万能的魔杖。
\n\n理想情况下,您\xe2\x80\x99d 为所有这些函数添加基于承诺的替代实现\xe2\x80\x93 流行的承诺库,如bluebird,已经为map和执行此操作reduce,例如\xe2\x80\x93 并使用async/await代替发电机(因为async函数返回 Promise):
async function asyncReduce() {\n const sum = await Promise.reduce(\n [1, 2, 3, 4, 5],\n async (m, n) => m + await delayed(n),\n 0\n );\n\n console.log(sum);\n}\nRun Code Online (Sandbox Code Playgroud)\n\n你\xe2\x80\x99不需要等待async如果 ECMAScript 有像 Python 这样健全的装饰器,
@Promise.coroutine\nfunction* add(m, n) {\n return m + (yield delayed(n));\n}\n\n@Promise.coroutine\nfunction* asyncReduce() {\n const sum = yield Promise.reduce([1, 2, 3, 4, 5], add, 0);\n\n console.log(sum);\n}\nRun Code Online (Sandbox Code Playgroud)\n\n\xe2\x80\xa6 但它没有\xe2\x80\x99t,所以你这样做了。或者你可以接受这样的代码:
\n\nconst asyncReduce = Promise.coroutine(function* () {\n const sum = yield Promise.reduce([1, 2, 3, 4, 5], Promise.coroutine(function* (m, n) {\n return m + (yield delayed(n));\n }), 0);\n\n console.log(sum);\n});\nRun Code Online (Sandbox Code Playgroud)\n