Javascript 如何在基于回调的循环中使用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

尽管yield关键字的主要用途是为某些数据提供迭代器,但使用它来创建异步循环也相当方便:

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
从其比较函数异步接受比较结果;你必须完全重新实现它。对于其他个别情况,可能会有黑客攻击,比如一个coroutiney
forEach
(它甚至不一定像您预期的那样工作,因为每个生成器都会运行到它的第一个
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);
});