Promise.then 的调度顺序承诺在 Promise.all 中分组时
The scheduling order of Promise.then promises when grouped in Promise.all
最近遇到一个很有意思的问题。背景是我想同时触发一堆http请求,并将每个响应的结果捕获到一个数组中。此外,当所有请求的承诺都得到解决时,.
我尝试了两种方法来实现它,但有时会得到不同的结果。
查看以下代码片段(httpHelper.get
只是 returns BlueBird 的承诺)。
解决方案 A:
function solutionA() {
var requestOptions = [...]; // an array of request options
var promises = [];
var results = [];
_.forEach(requestOptions, function(requestOption){
var promise = httpHelper.get(requestOption).then(function singleThenCallBack(response){
//Using this solution, sometimes this code won't execute from some response, I guess it's because the Promise.all.then gets executed before this then.
results.push(response.body.result);
});
promises.push(promise);
});
return Promise.all(promises).then(function allThenCallBack(){
return results;
});
}
方案B:
function solutionB() {
var requestOptions = [...]; // an array of request options
var promises = [];
var results = [];
_.forEach(requestOptions, function singleThenCallBack(requestOption){
var promise = httpHelper.get(requestOption);
promises.push(promise);
});
return Promise.all(promises).then(function allThenCallBack(responses){
_.forEach(responses, function(response){
results.push(response.body.result);
});
return results;
});
}
所以使用解决方案 A 的问题是,有时 singleThenCallBack
函数不会在某些响应中被调用,因此 results
不包含我应该得到的所有结果。
解决方案 B 始终确保将所有结果推入数组 results
。我想原因是 Promise
和 Promise.all
的 then
的时间安排。
我的问题是,在解决方案 A 中,promises
不应该是 Promise.then
的承诺,这将确保在 allThenCallBack
之前调用所有响应的 singleThenCallBack
函数功能达到了吗?
希望有人能向我解释这种行为的原因。谢谢!
编辑:
所以我尝试 运行 一些代码来证明使用以下代码链接的承诺:
var Promise = require("bluebird");
var promises = [];
var promise1 = new Promise(function(resolve, reject){
setTimeout(function() {
resolve(1);
}, 5000);
});
var thenPromise1 = promise1.then(function(value){
console.log("********* resolved promise1 *********: " + value);
console.log(promise1);
return 0.1;
}, console.log("*****This means then function is called synchronously."));
promises.push(thenPromise1);
thenPromise1.then(function(value){
console.log("********* resolved thenPromise1 *********: " + value);
console.log(thenPromise1);
});
var allPromise = Promise.all(promises);
allPromise.then(function(value){
console.log("********* resolved allPromise *********: " + value);
console.log(allPromise);
});
console.log("********* promise1 *********");
console.log(promise1);
console.log("********* thenPromise1 *********");
console.log(thenPromise1);
console.log("********* allPromise *********");
console.log(allPromise);
console.log("***********code end*************");
输出为:
*****This means then function is called synchronously.
********* promise1 *********
{ _bitField: 1,
_fulfillmentHandler0: [Function],
_rejectionHandler0: undefined,
_promise0:
{ '2': 0,
'3':
{ _promise: [Object],
_values: [Object],
_length: 1,
_totalResolved: 0 },
_bitField: 2,
_fulfillmentHandler0: [Function],
_rejectionHandler0: undefined,
_promise0:
{ _bitField: 0,
_fulfillmentHandler0: undefined,
_rejectionHandler0: undefined,
_promise0: undefined,
_receiver0: undefined,
_trace: [Object] },
_receiver0: undefined,
_trace: { [Error] _parent: undefined, _promisesCreated: 0, _length: 1 } },
_receiver0: undefined,
_trace: { [Error] _parent: undefined, _promisesCreated: 0, _length: 1 } }
********* thenPromise1 *********
{ '2': 0,
'3':
{ _promise:
{ _bitField: 134217729,
_fulfillmentHandler0: [Function],
_rejectionHandler0: undefined,
_promise0: [Object],
_receiver0: undefined },
_values: [ [Circular] ],
_length: 1,
_totalResolved: 0 },
_bitField: 2,
_fulfillmentHandler0: [Function],
_rejectionHandler0: undefined,
_promise0:
{ _bitField: 0,
_fulfillmentHandler0: undefined,
_rejectionHandler0: undefined,
_promise0: undefined,
_receiver0: undefined,
_trace: { [Error] _parent: undefined, _promisesCreated: 0, _length: 1 } },
_receiver0: undefined,
_trace: { [Error] _parent: undefined, _promisesCreated: 0, _length: 1 } }
********* allPromise *********
{ _bitField: 134217729,
_fulfillmentHandler0: [Function],
_rejectionHandler0: undefined,
_promise0:
{ _bitField: 0,
_fulfillmentHandler0: undefined,
_rejectionHandler0: undefined,
_promise0: undefined,
_receiver0: undefined,
_trace: { [Error] _parent: undefined, _promisesCreated: 0, _length: 1 } },
_receiver0: undefined }
***********code end*************
********* resolved promise1 *********: 1
{ _bitField: 33554433,
_fulfillmentHandler0: [Function],
_rejectionHandler0: 1,
_promise0: undefined,
_receiver0: undefined,
_trace: { [Error] _parent: undefined, _promisesCreated: 0, _length: 1 } }
********* resolved thenPromise1 *********: 0.1
{ '2': 0,
'3':
{ _promise:
{ _bitField: 134217729,
_fulfillmentHandler0: [Function],
_rejectionHandler0: undefined,
_promise0: [Object],
_receiver0: undefined },
_values: [ [Circular] ],
_length: 1,
_totalResolved: 0 },
_bitField: 33554434,
_fulfillmentHandler0: [Function],
_rejectionHandler0: 0.1,
_promise0: undefined,
_receiver0: undefined,
_trace: { [Error] _parent: undefined, _promisesCreated: 0, _length: 1 } }
********* resolved allPromise *********: 0.1
{ _bitField: 167772161,
_fulfillmentHandler0: [Function],
_rejectionHandler0: [ 0.1 ],
_promise0: undefined,
_receiver0: undefined }
在输出中,thenPromise1
对象具有 _promise0
,这是 thenPromise1.then(...)
创建的承诺。而 '3'
之后的 promise 对象是 Promise.all(...)
创建的 promise,这意味着这个 promise 总是链接在 thenPromise1
之后。
所以我认为这个问题是无效的,其他部分有问题。
这并不能解释OP的问题,但我不适合发表评论。
这没有意义。 solutionA
或 solutionB
应该有相同的结果。要么所有 Promise 都成功解决,然后 results.length
必须等于 requestOptions.length
或一个或多个 httpHelper.get(requestOption)
失败,然后 Promise.all(promises)
也将被拒绝并且不会返回 result
。所以我很确定问题出在其他地方。
除此之外,当您使用 bluebird 时,您可以以更清晰的方式编写代码(假设您使用 const Promise = require('bluebird')
:
function solution() {
var requestOptions = [...]; // an array of request options
return Promise.all(requestOptions) // pass all request options
.map(function(requestOption) { // for each option create a request and return its promise
return httpHelper.get(requestOption);
})
.map(function(response) { // for each response return the response.body.result
return response.body.result;
}); // now the promise resolves to an array containing the just the response.body.result
}
solutionA()
.then(function( result ) {
console.dir(result);
});
在 ES6 中你可以使用箭头函数这样写:
function solution() {
var requestOptions = [...]; // an array of request options
return Promise.all(requestOptions) // pass all request options
.map( requestOption => httpHelper.get(requestOption) )
.map( response => response.body.result );
}
初始承诺中的 .then()
处理程序将在 Promise.all()
.then()
处理程序执行之前执行。这是因为在 .then()
处理程序被调用之前,原始承诺不会完成和解决(因为它可能 return 更高级别的承诺在解决之前必须等待的链式承诺)。但是,那些原始的 .then()
处理程序不会以可预测的顺序执行,因为您的所有操作都是 运行 并行的,这意味着您在 solutionA 中的 results
数组没有顺序。
我建议您像这样更改原始承诺的结果:
_.forEach(requestOptions, function(requestOption){
var promise = httpHelper.get(requestOption).then(function singleThenCallBack(response){
// change resolved value to be the body.result
return response.body.result;
});
promises.push(promise);
});
return Promise.all(promises);
但是,如果您使用的是 Bluebird,您也可以使用 Promise.map()
,它结合了迭代和 Promise.all()
。
return Promise.map(requestOptions, function(item) {
return httpHelper.get(item).then(function(response) {
return response.body.result;
});
});
最近遇到一个很有意思的问题。背景是我想同时触发一堆http请求,并将每个响应的结果捕获到一个数组中。此外,当所有请求的承诺都得到解决时,. 我尝试了两种方法来实现它,但有时会得到不同的结果。
查看以下代码片段(httpHelper.get
只是 returns BlueBird 的承诺)。
解决方案 A:
function solutionA() {
var requestOptions = [...]; // an array of request options
var promises = [];
var results = [];
_.forEach(requestOptions, function(requestOption){
var promise = httpHelper.get(requestOption).then(function singleThenCallBack(response){
//Using this solution, sometimes this code won't execute from some response, I guess it's because the Promise.all.then gets executed before this then.
results.push(response.body.result);
});
promises.push(promise);
});
return Promise.all(promises).then(function allThenCallBack(){
return results;
});
}
方案B:
function solutionB() {
var requestOptions = [...]; // an array of request options
var promises = [];
var results = [];
_.forEach(requestOptions, function singleThenCallBack(requestOption){
var promise = httpHelper.get(requestOption);
promises.push(promise);
});
return Promise.all(promises).then(function allThenCallBack(responses){
_.forEach(responses, function(response){
results.push(response.body.result);
});
return results;
});
}
所以使用解决方案 A 的问题是,有时 singleThenCallBack
函数不会在某些响应中被调用,因此 results
不包含我应该得到的所有结果。
解决方案 B 始终确保将所有结果推入数组 results
。我想原因是 Promise
和 Promise.all
的 then
的时间安排。
我的问题是,在解决方案 A 中,promises
不应该是 Promise.then
的承诺,这将确保在 allThenCallBack
之前调用所有响应的 singleThenCallBack
函数功能达到了吗?
希望有人能向我解释这种行为的原因。谢谢!
编辑: 所以我尝试 运行 一些代码来证明使用以下代码链接的承诺:
var Promise = require("bluebird");
var promises = [];
var promise1 = new Promise(function(resolve, reject){
setTimeout(function() {
resolve(1);
}, 5000);
});
var thenPromise1 = promise1.then(function(value){
console.log("********* resolved promise1 *********: " + value);
console.log(promise1);
return 0.1;
}, console.log("*****This means then function is called synchronously."));
promises.push(thenPromise1);
thenPromise1.then(function(value){
console.log("********* resolved thenPromise1 *********: " + value);
console.log(thenPromise1);
});
var allPromise = Promise.all(promises);
allPromise.then(function(value){
console.log("********* resolved allPromise *********: " + value);
console.log(allPromise);
});
console.log("********* promise1 *********");
console.log(promise1);
console.log("********* thenPromise1 *********");
console.log(thenPromise1);
console.log("********* allPromise *********");
console.log(allPromise);
console.log("***********code end*************");
输出为:
*****This means then function is called synchronously.
********* promise1 *********
{ _bitField: 1,
_fulfillmentHandler0: [Function],
_rejectionHandler0: undefined,
_promise0:
{ '2': 0,
'3':
{ _promise: [Object],
_values: [Object],
_length: 1,
_totalResolved: 0 },
_bitField: 2,
_fulfillmentHandler0: [Function],
_rejectionHandler0: undefined,
_promise0:
{ _bitField: 0,
_fulfillmentHandler0: undefined,
_rejectionHandler0: undefined,
_promise0: undefined,
_receiver0: undefined,
_trace: [Object] },
_receiver0: undefined,
_trace: { [Error] _parent: undefined, _promisesCreated: 0, _length: 1 } },
_receiver0: undefined,
_trace: { [Error] _parent: undefined, _promisesCreated: 0, _length: 1 } }
********* thenPromise1 *********
{ '2': 0,
'3':
{ _promise:
{ _bitField: 134217729,
_fulfillmentHandler0: [Function],
_rejectionHandler0: undefined,
_promise0: [Object],
_receiver0: undefined },
_values: [ [Circular] ],
_length: 1,
_totalResolved: 0 },
_bitField: 2,
_fulfillmentHandler0: [Function],
_rejectionHandler0: undefined,
_promise0:
{ _bitField: 0,
_fulfillmentHandler0: undefined,
_rejectionHandler0: undefined,
_promise0: undefined,
_receiver0: undefined,
_trace: { [Error] _parent: undefined, _promisesCreated: 0, _length: 1 } },
_receiver0: undefined,
_trace: { [Error] _parent: undefined, _promisesCreated: 0, _length: 1 } }
********* allPromise *********
{ _bitField: 134217729,
_fulfillmentHandler0: [Function],
_rejectionHandler0: undefined,
_promise0:
{ _bitField: 0,
_fulfillmentHandler0: undefined,
_rejectionHandler0: undefined,
_promise0: undefined,
_receiver0: undefined,
_trace: { [Error] _parent: undefined, _promisesCreated: 0, _length: 1 } },
_receiver0: undefined }
***********code end*************
********* resolved promise1 *********: 1
{ _bitField: 33554433,
_fulfillmentHandler0: [Function],
_rejectionHandler0: 1,
_promise0: undefined,
_receiver0: undefined,
_trace: { [Error] _parent: undefined, _promisesCreated: 0, _length: 1 } }
********* resolved thenPromise1 *********: 0.1
{ '2': 0,
'3':
{ _promise:
{ _bitField: 134217729,
_fulfillmentHandler0: [Function],
_rejectionHandler0: undefined,
_promise0: [Object],
_receiver0: undefined },
_values: [ [Circular] ],
_length: 1,
_totalResolved: 0 },
_bitField: 33554434,
_fulfillmentHandler0: [Function],
_rejectionHandler0: 0.1,
_promise0: undefined,
_receiver0: undefined,
_trace: { [Error] _parent: undefined, _promisesCreated: 0, _length: 1 } }
********* resolved allPromise *********: 0.1
{ _bitField: 167772161,
_fulfillmentHandler0: [Function],
_rejectionHandler0: [ 0.1 ],
_promise0: undefined,
_receiver0: undefined }
在输出中,thenPromise1
对象具有 _promise0
,这是 thenPromise1.then(...)
创建的承诺。而 '3'
之后的 promise 对象是 Promise.all(...)
创建的 promise,这意味着这个 promise 总是链接在 thenPromise1
之后。
所以我认为这个问题是无效的,其他部分有问题。
这并不能解释OP的问题,但我不适合发表评论。
这没有意义。 solutionA
或 solutionB
应该有相同的结果。要么所有 Promise 都成功解决,然后 results.length
必须等于 requestOptions.length
或一个或多个 httpHelper.get(requestOption)
失败,然后 Promise.all(promises)
也将被拒绝并且不会返回 result
。所以我很确定问题出在其他地方。
除此之外,当您使用 bluebird 时,您可以以更清晰的方式编写代码(假设您使用 const Promise = require('bluebird')
:
function solution() {
var requestOptions = [...]; // an array of request options
return Promise.all(requestOptions) // pass all request options
.map(function(requestOption) { // for each option create a request and return its promise
return httpHelper.get(requestOption);
})
.map(function(response) { // for each response return the response.body.result
return response.body.result;
}); // now the promise resolves to an array containing the just the response.body.result
}
solutionA()
.then(function( result ) {
console.dir(result);
});
在 ES6 中你可以使用箭头函数这样写:
function solution() {
var requestOptions = [...]; // an array of request options
return Promise.all(requestOptions) // pass all request options
.map( requestOption => httpHelper.get(requestOption) )
.map( response => response.body.result );
}
初始承诺中的 .then()
处理程序将在 Promise.all()
.then()
处理程序执行之前执行。这是因为在 .then()
处理程序被调用之前,原始承诺不会完成和解决(因为它可能 return 更高级别的承诺在解决之前必须等待的链式承诺)。但是,那些原始的 .then()
处理程序不会以可预测的顺序执行,因为您的所有操作都是 运行 并行的,这意味着您在 solutionA 中的 results
数组没有顺序。
我建议您像这样更改原始承诺的结果:
_.forEach(requestOptions, function(requestOption){
var promise = httpHelper.get(requestOption).then(function singleThenCallBack(response){
// change resolved value to be the body.result
return response.body.result;
});
promises.push(promise);
});
return Promise.all(promises);
但是,如果您使用的是 Bluebird,您也可以使用 Promise.map()
,它结合了迭代和 Promise.all()
。
return Promise.map(requestOptions, function(item) {
return httpHelper.get(item).then(function(response) {
return response.body.result;
});
});