假设我有一个处理两个承诺的Promise.all()
。如果一个承诺产生错误,但另一个承诺解决了错误,我希望能够在Promise.all()
解决后根据情况处理错误。
ES6承诺错过了结算方法,我认为这是有充分理由的。但我不禁认为,.settle()
方法会使这个问题对我来说容易得多。
我是走错路了,还是用解决方法扩展ES6承诺是正确的呢?
我想如何使用.settle()
的一个例子
Promise.all([Action1,Action2])
.settle(function(arrayOfSettledValues)
//if 1 failed but not 2, handle
//if 2 failed but not 1, handle
//etc....
)
发布于 2016-04-13 09:24:45
我是走错路了,还是用解决方法扩展ES6承诺是正确的呢?
您不能直接使用Promise.all()
来生成.settle()
类型行为,因为Promise.all()
是“快速失败”,并且在第一个承诺拒绝时立即返回,并且它只返回拒绝原因,其他结果都不返回,从而获得所有结果--不管是否拒绝。
所以,我们需要一些不同的东西。通常情况下,解决该问题的最简单方法是向任何创建承诺数组的操作中添加一个.then()
处理程序,这样就可以捕获任何拒绝,并将它们转化为具有特定值的承诺,您可以对其进行测试。但是,这种类型的解决方案依赖于实现,因为它取决于您返回的值类型,因此这并不是完全通用的。
如果您想要一个通用的解决方案,那么像.settle()
这样的东西是非常有用的。
您不能使用该结构:
Promise.all([...]).settle(...).then(...);
备注(添加于2019年):看来承诺标准努力选择了 Promise.allSettled() 作为“类结算”行为的标准实现。在这个答案的末尾,您可以看到更多的信息。
因为当您传递第一个承诺时,Promise.all()
拒绝它,因此它只返回该拒绝。.settle()
逻辑的工作原理如下:
Promise.settle([...]).then(...);
而且,如果您感兴趣,下面是Promise.settle()
的一个相当简单的实现
// ES6 version of settle
Promise.settle = function(promises) {
function PromiseInspection(fulfilled, val) {
return {
isFulfilled: function() {
return fulfilled;
}, isRejected: function() {
return !fulfilled;
}, isPending: function() {
// PromiseInspection objects created here are never pending
return false;
}, value: function() {
if (!fulfilled) {
throw new Error("Can't call .value() on a promise that is not fulfilled");
}
return val;
}, reason: function() {
if (fulfilled) {
throw new Error("Can't call .reason() on a promise that is fulfilled");
}
return val;
}
};
}
return Promise.all(promises.map(function(p) {
// make sure any values are wrapped in a promise
return Promise.resolve(p).then(function(val) {
return new PromiseInspection(true, val);
}, function(err) {
return new PromiseInspection(false, err);
});
}));
}
在此实现中,Promise.settle()
将始终解析(永不拒绝),并使用一个PromiseInspection
对象数组进行解析,该数组允许您测试每个单独的结果,以查看其解析或拒绝,以及每个结果的值或原因。它的工作方式是将一个.then()
处理程序附加到传递给它的每个承诺,这些承诺处理该承诺的决定或拒绝,并将结果放入一个PromiseInspection
对象中,然后该对象成为承诺的解析值。
然后,您将像这样使用这个实现;
Promise.settle([...]).then(function(results) {
results.forEach(function(pi, index) {
if (pi.isFulfilled()) {
console.log("p[" + index + "] is fulfilled with value = ", pi.value());
} else {
console.log("p[" + index + "] is rejected with reasons = ", pi.reason());
}
});
});
FYI,我自己编写了另一个版本的.settle
,我称之为.settleVal()
,当您不需要实际的拒绝原因时,我发现使用起来更容易,您只是想知道某个给定的数组插槽是否被拒绝。在此版本中,您将传递一个默认值,该默认值应替代任何拒绝的承诺。然后,您只会得到一个返回值的平面数组,并将其设置为默认值,其中任何值都是被拒绝的。例如,您通常可以选择一个rejectVal
of null
、0
、""
或{}
,这样就可以更容易地处理结果。以下是功能:
// settle all promises. For rejected promises, return a specific rejectVal that is
// distinguishable from your successful return values (often null or 0 or "" or {})
Promise.settleVal = function(rejectVal, promises) {
return Promise.all(promises.map(function(p) {
// make sure any values or foreign promises are wrapped in a promise
return Promise.resolve(p).then(null, function(err) {
// instead of rejection, just return the rejectVal (often null or 0 or "" or {})
return rejectVal;
});
}));
};
然后,你就这样用它:
Promise.settleVal(null, [...]).then(function(results) {
results.forEach(function(pi, index) {
if (pi !== null) {
console.log("p[" + index + "] is fulfilled with value = ", pi);
}
});
});
这并不是.settle()
的全部替代品,因为有时您可能想知道它被拒绝的实际原因,或者很难区分被拒绝的值和未拒绝的值。但是,我发现90%以上的时间,这是比较简单的使用。
下面是我对.settle()
的最新简化,它将返回数组中的instanceof Error
作为区分已解析值和拒绝错误的方法:
// settle all promises. For rejected promises, leave an Error object in the returned array
Promise.settleVal = function(promises) {
return Promise.all(promises.map(function(p) {
// make sure any values or foreign promises are wrapped in a promise
return Promise.resolve(p).catch(function(err) {
let returnVal = err;
// instead of rejection, leave the Error object in the array as the resolved value
// make sure the err is wrapped in an Error object if not already an Error object
if (!(err instanceof Error)) {
returnVal = new Error();
returnVal.data = err;
}
return returnVal;
});
}));
};
然后,你就这样用它:
Promise.settleVal(null, [...]).then(function(results) {
results.forEach(function(item, index) {
if (item instanceof Error) {
console.log("p[" + index + "] rejected with error = ", item);
} else {
console.log("p[" + index + "] fulfilled with value = ", item);
}
});
});
对于所有情况,这都可以完全替代.settle()
,只要instanceof Error
永远不是您的承诺的解析值(它确实不应该是)。
承诺标准努力
到2019年,.allSettled()
似乎正在成为这类行为的标准。还有,这是一个多填区:
if (!Promise.allSettled) {
Promise.allSettled = function(promises) {
let wrappedPromises = Array.from(promises).map(p =>
this.resolve(p).then(
val => ({ state: 'fulfilled', value: val }),
err => ({ state: 'rejected', reason: err })
)
);
return this.all(wrappedPromises);
}
}
用法如下:
let promises = [...]; // some array of promises, some of which may reject
Promise.allSettled(promises).then(results => {
for (let r of results) {
if (r.state === 'fulfilled') {
console.log('fulfilled:', r.val);
} else {
console.log('rejected:', r.err);
}
}
});
请注意,Promise.allSettled()
本身总是解决问题,从不拒绝,尽管后续的.then()
处理程序可能会抛出或返回被拒绝的承诺,从而使整个链拒绝。
截至2019年6月,这还没有在目前的桌面Chrome浏览器,但计划在即将发布(例如,在2019年晚些时候)。
https://stackoverflow.com/questions/36605253
复制相似问题