嵌套的 Promise 执行不同步

Nested Promise execution is out of sync

我有一个简单的设置,如 this fiddle 所示:

var doDelay = function(who) {
    return Promise.delay(50)
        .tap(function() {
            console.log(who + ' done');
        });
};

Promise.resolve()
    .then(doDelay('a'))
    .tap(function() {
        console.log('a done2');
    })
    .then(doDelay('b'))
    .tap(function() {
        console.log('b done2');
    })
    .then(function() {
        console.log('all done!');
    });

输出为:

a done2
b done2
all done!
a done
b done

但我预计:

a done
b done
a done2
b done2
all done!

我做错了什么?

您不得向 then 传递承诺(立即 调用 delayMany 的结果)。相反,它期望在上下文承诺解析时执行回调 function。您需要将 delayMany 调用包装在函数表达式中:

Promise.resolve()
.then(function(undefined) { return delayMany(xsa); })
.tap(function(xsa) { console.log(xsa[0] + ' done2'); })
.then(function(xsa) { return delayMany(xsb) })
.tap(function(xsb) { console.log(xsb[0] + ' done2'); })
.then(function(xsb) { console.log('all done!'); });

正如 Bergi 指出的那样,将承诺作为参数传递给 then 是无效的,但即使它被允许,另一个问题是一旦您调用 delayMany('a')Promise.delay(50) 在里面它会开始执行,这不是你想要做的。

您可以将调用包装在匿名函数中,或者使用 .bind():

var delayMany = function(who) {
    return Promise.delay(50)
    .tap(function() {
        console.log(who  + ' done');
    });
};

Promise.resolve()
.then(delayMany.bind(null, 'a'))
.tap(function() { console.log('a done2'); })

.then(delayMany.bind(null, 'b'))
.tap(function() { console.log('b done2'); })

.then(function() {
    console.log('all done!');
});

问:“当我向它传递承诺时,为什么 then 没有抛出错误?”

答案在 Promises/A+ spec:

A promise’s then method accepts two arguments:

promise.then(onFulfilled, onRejected)

2.2.1. Both onFulfilled and onRejected are optional arguments:

2.2.1.1. If onFulfilled is not a function, it must be ignored.

由于您使用的是 bluebird,因此您只需使用 .delay 方法即可:

Promise.resolve()
    .delay('a', 50)
    .tap(function() {
        console.log('a done2');
    })
    .delay('b', 50)
    .tap(function() {
        console.log('b done2');
    })
    .then(function() {
        console.log('all done!');
    });