【问题标题】:Promise.all not valid to iterate through generatorsPromise.all 对遍历生成器无效
【发布时间】:2016-11-05 06:34:45
【问题描述】:

试图了解 Javascript 生成器和 Promise,我已经检查过它们是很好的盟友。我需要遍历承诺的协程(来自 Bluebird 库的Promise.coroutine)可以轻松地以正确的顺序执行一些承诺。使用这段代码(对不起,延迟的反模式,我稍后会学会避免它):

function myPromise(x,time,i){
    var deferred = Q.defer();

    setTimeout(() => {
        deferred.resolve(x + i);
    },time);

    return deferred.promise;
}

router.get('/', function(req, res, next) {
    for (var i = 0; i < 5; i++) {      
        Promise.coroutine(function*(i) {
            var a = yield myPromise('a',6000,i);
            var b = yield myPromise('b',1000,i);
            console.log(a,b);
        })(i)
        .then(() => {
            console.log('Then');
        }).
        catch((err) => next(err));
    }
});

控制台的输出(几乎)正确:

a0 b0
a1 b1
a2 b2
Then
Then
Then
a3 b3
a4 b4
Then
Then

检查一下,我的 for 循环似乎不太好,因为由于 Then,一些承诺在其他承诺之前结束。

如果我在承诺中包含if(i == 3) deferred.reject(new Error(' ERROR!!'));,则仅针对该承诺而不是其他承诺引发错误,并且在其他承诺之后引发错误:

ERROR!!
a0 b0
Then
a1 b1
a2 b2
Then
Then
a4 b4
Then

我认为使用 for 循环进行迭代永远不会成为此类问题的解决方案。进行了更多研究,我尝试使用Promise.all 对Promise.coroutine 进行一系列调用:

    Promise
        .all([
            Promise.coroutine(1),
            Promise.coroutine(2),
            Promise.coroutine(3)
        ])
        .then(() => {
            console.log('then');
        })
        .catch((err) => next(err));

但在这种情况下,我认为错误:

generatorFunction 必须是函数

如果我这样做:

var coroutine = function* coroutine(i) {
    var a = yield myPromise('a',6000,i);
    var b = yield myPromise('b',1000,i);
    console.log(a,b);
};

同样的generatorFunction 必须是函数。

您知道这里出了什么问题,或者是否有比Promise.all 更好的“迭代”方式?

【问题讨论】:

    标签: javascript node.js promise generator bluebird


    【解决方案1】:

    对延迟的反模式感到抱歉

    实际上你没有在myPromise 中使用the deferred antipattern,使用deferreds 从像setTimeout 这样的带回调的异步函数中获得promise 完全没问题。您可以改用Promise 构造函数,但这并不是绝对必要的。最好的解决方案当然是 Promise.delay (Bluebird) 或 Q.delay (Q) :-)

    我的 for 循环似乎不太好,因为一些承诺在其他承诺之前结束

    那是因为您对协程(执行异步操作)的调用在循环内。如果您将循环放在生成器函数中,它将按预期工作:

    router.get('/', function(req, res, next) {
        Promise.coroutine(function*() {
            for (var i = 0; i < 5; i++) {      
                var a = yield myPromise('a',6000,i);
                var b = yield myPromise('b',1000,i);
                console.log(a,b);
                console.log('Then');
            }
        })()
        .catch((err) => next(err));
    });
    

    甚至更好:

    var run = Promise.coroutine(function*(req, res, next) {
        for (var i = 0; i < 5; i++) {      
            var a = yield myPromise('a',6000,i);
            var b = yield myPromise('b',1000,i);
            console.log(a,b);
            console.log('Then');
        }
    });
    router.get('/', function(req, res, next) {
        run(req, res, next).catch(next);
    });
    

    【讨论】:

    • 通常不鼓励将协程创建为在每次迭代中运行的匿名函数。最好是创建一次然后传入。
    • @BenjaminGruenbaum:我考虑过包含它,但认为没有必要。现在添加:-)
    猜你喜欢
    • 2016-08-22
    • 2020-12-21
    • 1970-01-01
    • 2014-07-20
    • 2018-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-28
    相关资源
    最近更新 更多