Javascript 如何在基于回调的循环中使用yield?
尽管yield关键字的主要用途是为某些数据提供迭代器,但使用它来创建异步循环也相当方便:Javascript 如何在基于回调的循环中使用yield?,javascript,arrays,asynchronous,yield,Javascript,Arrays,Asynchronous,Yield,尽管yield关键字的主要用途是为某些数据提供迭代器,但使用它来创建异步循环也相当方便: function* bigLoop() { // Some nested loops for( ... ) { for( ... ) { // Yields current progress, eg. when parsing file // or processing an image
function* bigLoop() {
// Some nested loops
for( ... ) {
for( ... ) {
// Yields current progress, eg. when parsing file
// or processing an image
yield percentCompleted;
}
}
}
然后可以异步调用:
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);
}
然而,在现代javascript中,基于回调的循环已经非常流行。我们有Array.prototype.forEach
,Array.prototype.find
或Array.prototype.sort
。所有这些都基于为每个迭代传递的回调。我甚至听到有人建议我们在可能的情况下使用它们,因为它们可以比标准循环优化得更好
我还经常使用基于回调的循环来抽象出一些复杂的循环模式
这里的问题是,有没有可能将它们转换成基于的迭代器?作为一个简单的例子,我想让你异步排序一个数组。
tl;dr:您不能这样做,但请查看最新的V8和:
async function asyncReduce() {
const sum = await Promise.reduce(
[1, 2, 3, 4, 5],
async (m, n) => m + await Promise.delay(200, n),
0
);
console.log(sum);
}
不,无法使Array.prototype.sort
从其比较函数异步接受比较结果;你必须完全重新实现它。对于其他个别情况,可能会有黑客攻击,比如一个coroutineyforEach
(它甚至不一定像您预期的那样工作,因为每个生成器都会运行到它的第一个yield
,然后从yield
继续运行):
和reduce
,这在本质上起到了很好的作用(直到您看到性能):
但是,所有的功能都没有魔杖
理想情况下,您应该为所有这些函数添加替代的基于承诺的实现—例如,流行的承诺库,如bluebird,已经为map
和reduce
这样做了—并使用async
/wait
而不是生成器(因为async
函数返回承诺):
如果ECMAScript有像Python这样的健全的装饰器,那么您就不需要等待async
支持来做这么多:
@Promise.coroutine
function* add(m, n) {
return m + (yield delayed(n));
}
@Promise.coroutine
function* asyncReduce() {
const sum = yield Promise.reduce([1, 2, 3, 4, 5], add, 0);
console.log(sum);
}
……但事实并非如此,你也是。或者,您可以使用以下代码:
const asyncReduce = Promise.coroutine(function* () {
const sum = yield Promise.reduce([1, 2, 3, 4, 5], Promise.coroutine(function* (m, n) {
return m + (yield delayed(n));
}), 0);
console.log(sum);
});
异步排序一个数组(使用一个生成器和一个js线程)会有什么好处吗?@davintroon这是一个例子,例子不是为了实用,而是为了易于理解。无论如何,足够长的数组可能需要太长的时间进行排序,例如,可能会断开用户与服务器的连接或延迟用户的浏览器。@TomášZato这是一个您可能会感兴趣的问题(不幸的标题)。所以,对你的作品的简短回答是,它是可以做到的。@Davidtroon我读了你建议的文章。对于生成器来说,这是一篇非常好的文章,但我认为它不包含任何解决此问题的方法。
function syncReduce() {
const sum = [1, 2, 3, 4, 5].reduce(function (m, n) {
return m + n;
}, 0);
console.log(sum);
}
function* asyncReduce() {
const sum = yield* [1, 2, 3, 4, 5].reduce(function* (m, n) {
return (yield* m) + (yield delayed(n));
}, function* () { return 0; }());
console.log(sum);
}
async function asyncReduce() {
const sum = await Promise.reduce(
[1, 2, 3, 4, 5],
async (m, n) => m + await delayed(n),
0
);
console.log(sum);
}
@Promise.coroutine
function* add(m, n) {
return m + (yield delayed(n));
}
@Promise.coroutine
function* asyncReduce() {
const sum = yield Promise.reduce([1, 2, 3, 4, 5], add, 0);
console.log(sum);
}
const asyncReduce = Promise.coroutine(function* () {
const sum = yield Promise.reduce([1, 2, 3, 4, 5], Promise.coroutine(function* (m, n) {
return m + (yield delayed(n));
}), 0);
console.log(sum);
});