为什么Javascript的承诺解析是乱序的?

Why are Javascript promises resolving out of order?

本文关键字:Javascript 承诺 为什么      更新时间:2023-09-26

我试图在一个项目中使用JavaScript承诺,事件的顺序是意想不到的。我已经把它缩小到一个使用测试承诺的小演示。

testPromises = function(promiseNum){
    return new Promise(function(resolve, reject) {
        console.log ('handling promise '+promiseNum);
        setTimeout(function(){
            console.log("resolving testPromises "+promiseNum);
            resolve();
        },2000)
    });
};

然后我这样称呼它:

testPromises(1).then(testPromises(2)).then(testPromises(3))
.then(function(value) {
        console.log('all promises resolved');
}, function(reason) {
        console.log('some promise was rejected');
});

这是控制台输出:

handling promise 1
handling promise 2
handling promise 3
resolving testPromises 1
all promises resolved
resolving testPromises 2
resolving testPromises 3

如何得到以下输出:

handling promise 1
resolving testPromises 1
handling promise 2
resolving testPromises 2
handling promise 3
resolving testPromises 3
all promises resolved

.then()需要一个函数引用。当你做这样的事情时:

.then(testPromises(2))

立即执行函数testPromise()并将返回值传递给.then()。这几乎从来都不是你想要的(除非testPromises()返回另一个函数),因为.then()做它的工作,你必须传递给它一个函数引用(一个函数,它可以在以后的某个时候调用)。如果你立即执行这个函数,那么.then()就不能完成它的工作,以后再调用它。

相反,你想要的是:

.then(function() {
    return testPromises(2);
})

或者,您可以使用.bind():

.then(testPromises.bind(null, 2))

所以,你的整个链看起来是这样的:

testPromises(1).then(function() {
    return testPromises(2);
}).then(function() {
    return testPromises(3);
}).then(function(value) {
    console.log('all promises resolved');
}, function(reason) {
    console.log('some promise was rejected');
});

或者,使用.bind()

testPromises(1)
  .then(testPromises.bind(null, 2))
  .then(testPromises.bind(null, 3))
  .then(function(value) {
        console.log('all promises resolved');
  }, function(reason) {
        console.log('some promise was rejected');
  });

如果您经常这样做,您可以为testPromises()创建一个curry包装器,它将返回另一个函数。这实际上就是上面的.bind()所做的,但是在声明了包装器函数之后,使用它的语法会更美观一些。

function tp(arg) {
    return function() {
        return testPromises(arg);
    }
}

然后,由于包装器返回另一个函数,您可以这样做:

testPromises(1).then(tp(2)).then(tp(3))
.then(function(value) {
    console.log('all promises resolved');
}, function(reason) {
    console.log('some promise was rejected');
});

这是获得所需输出的代码:

testPromises(1).then(function(){
  return testPromises(2);
}).then(function(){
  return testPromises(3);
}).then(function(value) {
  console.log('all promises resolved');
}, function(reason) {
  console.log('some promise was rejected');
});

承诺是异步解析的,但是同步创建的

testPromises(1).then(testPromises(2)).then(testPromises(3))

差不多
p1 = testPromises(1)
p2 = testPromises(2)
p3 = testPromises(3)
p1.then(p2).then(p3)

由于new Promise是同步的,您将看到的前三行将是

handling promise 1
handling promise 2
handling promise 3

嗯,

handling promise 1
handling promise 2
handling promise 3

首先出现,因为这些调用独立于Promise工作流,它们在创建每个Promise对象时执行,因为要这样做,您需要显式执行每个函数,如testPromises(1)等。

但是问题是你对console.log('all promises resolved');的调用只依赖于testPromises(1)被解决,所有其他的承诺都是分离的。

为了同步它们,每个.then调用必须返回一个Promise,你可以通过使用function并返回testPromises函数已经返回的Promises来做到这一点。

testPromises = function(promiseNum){
    return new Promise(function(resolve, reject) {
        console.log ('handling promise '+promiseNum);
        setTimeout(function(){
            console.log("resolving testPromises "+promiseNum);
            resolve();
        },0)
    });
};
testPromises(1).then(function() {
  return testPromises(2);
}).then(function() {
  return testPromises(3);
}).then(function(value) {
        console.log('all promises resolved');
}, function(reason) {
        console.log('some promise was rejected');
});