我误解了Promise.all吗?我在数组中有X个promise,我试图聚合数组的成功/失败率。
以下是我认为我知道的事情:
Promise.all采取一系列承诺。
如果所有承诺都成功,则运行.then
回调。
如果其中一个promise失败,则调用.catch
回调,传入的参数是单个引发错误的值。
如果有些成功并且有些失败,则没有触发回调,这是所有承诺的结果。即它不能给你一个类似(伪代码)[success, fail, success, success]
的数组 - 就像人们所期望的那样,可以在许多JS库中找到它们(ajax,ember等)。
就像.then
更像是.success
,而不是总是在完成所有承诺后运行的功能,无论某些成功还是失败。< / strong>为什么没有.when
.finally
.runThisShizNoMatterWhat
??或者我错过了什么(非常可能)?
答案 0 :(得分:13)
这与Bluebird Promise.all - multiple promises completed aggregating success and rejections有关,但与蓝鸟有关。问题的核心是,如果你想检查某些事情是成功还是失败,那么你并不是真的要求每个承诺的直接结果。相反,您希望在使用Promise.all
之前转换承诺。这个ES6标准的承诺没有帮助,但实现起来却微不足道。在大多数库中,这称为Promise.settle
。例如
var someThings = [...]; // some list of promises that may succeed or fail
settle(someThings).then(results => {
results.forEach(result => {
if (result.state === 'fullfilled'){
console.log('succeeded', result.value);
} else {
console.log('failed', result.value);
}
});
});
function settle(arr){
return Promise.all(arr.map(promise => {
return promise.then(
value => ({state: 'fullfilled', value}),
value => ({state: 'rejected', value})
);
}));
}
答案 1 :(得分:3)
如果其中一个承诺拒绝,则Promise.all
返回的承诺将被拒绝。所以拒绝处理程序将在其中一个承诺拒绝后立即被调用。如果您只是想在不担心拒绝的情况下运行所有承诺(即,如果任何承诺拒绝,请不要拒绝承诺),这可能不是理想的行为。
您仍然可以处理每个承诺拒绝,以便在拒绝后履行承诺。
var promiseRejected = new Promise(function(resolve, reject){
setTimeout(function(){
reject('I was rejected');
}, 1000);
});
promiseRejected = promiseRejected.then(null, function(reason){
//I was rejected, now lets fullfill it.
return reason;
});
var promiseResolved = new Promise(function(resolve, reject){
setTimeout(function(){
resolve('All good');
}, 1500);
});
var time = performance.now();
Promise.all([promiseRejected, promiseResolved]).then(function(results){
//both promises fulfilled; 1500 msecs passed
console.log(results[0], results[1], performance.now() - time);
});
&#13;
一个promise构造函数,在所有promise都被解析/拒绝时解析,例如:
Promise.when = function (arrPromises) {
if (!Array.isArray(arrPromises)) {
return new TypeError('Expecting an Array of Promises');
}
return new Promise(function (resolve, reject) {
var len = arrPromises.length,
values = [],
settled = 0;
function settle(value, index) {
values[index] = value;
settled++;
if (len === settled) {
resolve(values);
}
}
if (len === 0) {
resolve([]);
} else {
arrPromises.forEach(function (promise, index) {
var handler = function (value) {
settle(value, index);
};
promise.then(handler, handler);
});
}
});
}
答案 2 :(得分:3)
如果除了你的价值观之外你还能说错误,那么做你想做的事就像这样简单:
Promise.all(array.map(promise => promise.catch(error => error)))
var log = msg => div.innerHTML += "<p>" + msg + "</p>";
var a = () => Promise.resolve(1);
var b = () => Promise.reject("error");
var c = () => Promise.resolve(3);
Promise.all([a(), b(), c()].map(p => p.catch(e => e))).then(r => log(r));
&#13;
<div id="div"></div>
&#13;
答案 3 :(得分:2)
Promise.all
创建了一个新的承诺,只能作为一个整体解决或拒绝。也许你可以认为它具有every
数组方法语义,当第一个元素与谓词不匹配时,它返回false。
then
函数最多需要两个参数,第二个参数是被拒绝的处理程序。从这个意义上讲,它不仅仅是success
,它实际上可以处理所有情况。 catch
只是一种便捷方法,是.then(undefined, function(reason) { ... })
的缩写。
promise API没有您需要的东西我担心,您必须自己实现它。
答案 4 :(得分:1)
从您的问题看来,您似乎希望解决所有承诺,promise.all
方法无法保证,只有方法promise.settle
才能保证解决数组中的每个承诺。
如果您想要promise.all
的结果,同时还要解决每个承诺,并且通知已解决或拒绝了哪个承诺,那么方法spex.batch正是您所需要的。
从Batch Processing复制的示例:
var spex = require('spex')(Promise);
// function that returns a promise;
function getWord() {
return Promise.resolve("World");
}
// function that returns a value;
function getExcl() {
return '!';
}
// function that returns another function;
function nested() {
return getExcl;
}
var values = [
123,
"Hello",
getWord,
Promise.resolve(nested)
];
spex.batch(values)
.then(function (data) {
console.log("DATA:", data);
}, function (reason) {
console.log("REASON:", reason);
});
输出:
DATA: [ 123, 'Hello', 'World', '!' ]
现在让我们通过将getWord
改为此来使其失败:
function getWord() {
return Promise.reject("World");
}
现在输出是:
REASON: [ { success: true, result: 123 },
{ success: true, result: 'Hello' },
{ success: false, result: 'World' },
{ success: true, result: '!' } ]
即。整个数组结算,报告索引限制结果。
如果不是报告完整原因,我们会调用getErrors()
:
console.log("REASON:", reason.getErrors());
然后输出将是:
REASON: [ 'World' ]
这只是为了简化对发生的错误列表的快速访问。
你可以从method's protocol看到你可以传递的可选cb
- 回调参数,该参数将告诉你哪些承诺已经解决,哪些承诺被拒绝。
答案 5 :(得分:0)
我同意如果您使用Bluebird,使用Bluebird.reflect实现结算是最好的方法。根据您的使用案例,这是另一种可能很好的解决方案。它在一个有点有趣的情况下对我有用。这也假设Bluebird是承诺库。
就我而言,我有一系列函数(“任务”),这些函数都包含在Promise.method中。这些任务可能会返回被拒绝的承诺,或者它们可能返回或抛出将成为承诺解决方案的普通值。我需要执行所有这些并收集所有结果(不仅仅是第一次失败)。
同样,请记住,tasks数组中的每个项都是Promise.method包装函数。 (易于实施。请参阅http://bluebirdjs.com/docs/api/promise.method.html)
var runTasks = function(tasks) {
return Bluebird.reduce(tasks, function (results, task) {
return task()
.then(function (result) {
results.success.push(result)
return results
})
.caught(function (result) {
results.fail.push(result)
return results
})
}, { success: [], fail: [] })
}
然后你会这样调用它,找回一个具有一系列已完成值和一系列被拒绝值的对象:
runTasks(taskArray)
.then(function(results){
// do whatever you want here
// results.success is an array of the returned/resolved values
// results.fail is an array of the rejected/thrown values
})