假设我有一组promise正在发出网络请求,其中一个将失败:
// http://does-not-exist will throw a TypeError
var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]
Promise.all(arr)
.then(res => console.log('success', res))
.catch(err => console.log('error', err)) // This is executed
假设我想要等到所有这些都完成,不管是否有一个失败了。可能有一个资源的网络错误,我可以没有,但如果我能得到,我想在继续之前。我想优雅地处理网络故障。
因为承诺。所有这些都没有留下任何空间,在不使用承诺库的情况下,处理这个问题的推荐模式是什么?
与其拒绝,不如用一个对象来解决。
当你实现承诺时,你可以这样做
Const promise = arg => {
返回新的承诺((resolve, reject) => {
setTimeout(() => {
尝试{
If (arg != 2)
返回解析({success: true, data: arg});
其他的
抛出新的错误(arg)
}捕捉(e) {
返回解析({success: false, error: e, data: arg})
}
}, 1000);
})
}
Promise.all([1、2、3、4、5)。Map (e => promise(e)))。然后(d => console.log(d))
我知道这个问题有很多答案,我确信一定(如果不是全部)是正确的。
然而,我很难理解这些答案的逻辑/流程。
因此,我查看了Promise.all()的原始实现,并尝试模仿该逻辑-除了如果一个Promise失败不停止执行之外。
public promiseExecuteAll(promisesList: Promise<any>[] = []): Promise<{ data: any, isSuccess: boolean }[]>
{
let promise: Promise<{ data: any, isSuccess: boolean }[]>;
if (promisesList.length)
{
const result: { data: any, isSuccess: boolean }[] = [];
let count: number = 0;
promise = new Promise<{ data: any, isSuccess: boolean }[]>((resolve, reject) =>
{
promisesList.forEach((currentPromise: Promise<any>, index: number) =>
{
currentPromise.then(
(data) => // Success
{
result[index] = { data, isSuccess: true };
if (promisesList.length <= ++count) { resolve(result); }
},
(data) => // Error
{
result[index] = { data, isSuccess: false };
if (promisesList.length <= ++count) { resolve(result); }
});
});
});
}
else
{
promise = Promise.resolve([]);
}
return promise;
}
解释:
-循环输入的许诺列表并执行每个许诺。
—无论Promise是被解决还是被拒绝:将Promise的结果按照索引保存为结果数组。还保存解析/拒绝状态(isSuccess)。
-一旦所有承诺完成,将一个承诺与所有其他承诺的结果一起返回。
使用示例:
const p1 = Promise.resolve("OK");
const p2 = Promise.reject(new Error(":-("));
const p3 = Promise.resolve(1000);
promiseExecuteAll([p1, p2, p3]).then((data) => {
data.forEach(value => console.log(`${ value.isSuccess ? 'Resolve' : 'Reject' } >> ${ value.data }`));
});
/* Output:
Resolve >> OK
Reject >> :-(
Resolve >> 1000
*/
我认为下面提供了一个稍微不同的方法……比较fn_fast_fail()和fn_slow_fail()…尽管后者并没有因此而失败……你可以检查a和b中是否有一个或两个都是Error实例,如果你想让它到达catch块,就抛出这个错误(例如if (b instanceof Error) {throw b;})。参见jsfiddle。
var p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('p1_delayed_resolvement'), 2000);
});
var p2 = new Promise((resolve, reject) => {
reject(new Error('p2_immediate_rejection'));
});
var fn_fast_fail = async function () {
try {
var [a, b] = await Promise.all([p1, p2]);
console.log(a); // "p1_delayed_resolvement"
console.log(b); // "Error: p2_immediate_rejection"
} catch (err) {
console.log('ERROR:', err);
}
}
var fn_slow_fail = async function () {
try {
var [a, b] = await Promise.all([
p1.catch(error => { return error }),
p2.catch(error => { return error })
]);
console.log(a); // "p1_delayed_resolvement"
console.log(b); // "Error: p2_immediate_rejection"
} catch (err) {
// we don't reach here unless you throw the error from the `try` block
console.log('ERROR:', err);
}
}
fn_fast_fail(); // fails immediately
fn_slow_fail(); // waits for delayed promise to resolve
Benjamin Gruenbaum的回答当然很好。但我也能看出内森哈根的观点在抽象层面上显得模糊。拥有像e和v这样的短对象属性也没有帮助,但当然这是可以改变的。
在Javascript中有一个标准的Error对象,称为Error。理想情况下,您总是抛出该实例/后代。这样做的好处是您可以使用instanceof Error,并且您知道某些东西是错误的。
利用这个想法,下面是我对这个问题的看法。
基本上捕捉错误,如果错误不是error类型,则将错误包装在error对象中。结果数组将具有已解析的值或可以检查的Error对象。
catch内部的instanceof,是为了防止你使用一些外部库可能会reject("error"),而不是reject(new error ("error"))。
当然,如果您解决了一个错误,您也可以有承诺,但在这种情况下,无论如何都应该将其视为错误,就像最后一个示例所示的那样。
这样做的另一个好处是,数组析构保持简单。
const [value1, value2] = PromiseAllCatch(promises);
if (!(value1 instanceof Error)) console.log(value1);
而不是
const [{v: value1, e: error1}, {v: value2, e: error2}] = Promise.all(reflect..
if (!error1) { console.log(value1); }
你可能会说!error1检查比instanceof简单,但你也必须销毁v和e。
function PromiseAllCatch(promises) {
return Promise.all(promises.map(async m => {
try {
return await m;
} catch(e) {
if (e instanceof Error) return e;
return new Error(e);
}
}));
}
async function test() {
const ret = await PromiseAllCatch([
(async () => "this is fine")(),
(async () => {throw new Error("oops")})(),
(async () => "this is ok")(),
(async () => {throw "Still an error";})(),
(async () => new Error("resolved Error"))(),
]);
console.log(ret);
console.log(ret.map(r =>
r instanceof Error ? "error" : "ok"
).join(" : "));
}
test();
我最近建立了一个库,可以满足你的需要。它并行执行承诺,如果一个承诺失败,进程继续,最后它返回一个包含所有结果的数组,包括错误。
https://www.npmjs.com/package/promise-ax
我希望这对某些人有帮助。
const { createPromise } = require('promise-ax');
const promiseAx = createPromise();
const promise1 = Promise.resolve(4);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, new Error("error")));
const promise3 = Promise.reject("error");
const promise4 = promiseAx.resolve(8);
const promise5 = promiseAx.reject("errorAx");
const asyncOperation = (time) => {
return new Promise((resolve, reject) => {
if (time < 0) {
reject("reject");
}
setTimeout(() => {
resolve(time);
}, time);
});
};
const promisesToMake = [promise1, promise2, promise3, promise4, promise5, asyncOperation(100)];
promiseAx.allSettled(promisesToMake).then((results) => results.forEach((result) => console.log(result)));
// Salida esperada:
// 4
// Error: error
// error
// 8
// errorAx
// 100