问题描述
我无法正确捕获承诺链中的错误/拒绝。
const p1 = () => {
return new Promise((resolve,reject) => {
console.log("P1");
resolve();
});
};
const p2 = () => {
return new Promise((resolve,reject) => {
console.log("P2");
reject();
});
};
const p3 = () => {
return new Promise((resolve,reject) => {
console.log("P3");
resolve();
});
};
p1().catch(() => {
console.log("Caught p1");
}).then(p2).catch(() => {
console.log("Caught p2");
}).then(p3).catch(() => {
console.log("Caught p3");
}).then(() => {
console.log("Final then");
});
当 promise 被拒绝时,仍然会执行以下 .then
。据我了解,当在承诺链中发生错误/拒绝时,不会再执行其后的 .then
调用。
P1
P2
Caught p2
P3
Final then
拒绝被正确捕获,但为什么在捕获后记录“P3”?
我做错了什么?
澄清@evolutionxBox,这是我的预期结果:
Promise.resolve().then(() => {
console.log("resolve #1");
return Promise.reject();
}).then(() => {
console.log("resolve #2");
return Promise.resolve();
}).then(() => {
console.log("resolve #3");
return Promise.resolve();
}).then(() => {
console.log("Final end");
}).catch(() => {
console.log("Caught");
});
这段代码的工作原理完全一样。我看不出我的代码有什么不同,只是我单独声明了这些函数。
无论 Promise 在哪里被拒绝,上面的代码都会停止。
解决方法
试试这个。
const p1 = (arg) => {
// Promise returns data in the respected arguments
return new Promise((resolve,reject) => {
// Data to be accessed through first argument.
resolve(arg);
});
};
const p2 = (arg) => {
return new Promise((resolve,reject) => {
// Data to be accessed through second argument.
reject(arg);
});
}
p1('p1').then(resolve => {
console.log(resolve + ' is handled with the resolve argument. So it is accessed with .then()');
}) // Since reject isn't configured to pass any data we don't use .catch()
p2('p2').catch(reject => {
console.log(reject + ' is handled with the reject argument. So it is accessed with .catch()');
}) // Since resolve ins't configured to pass any data we don't use .then()
// You would normally configure a Promise to return a value on with resolve,and access it with .then() when it completes a task successfully.
// .catch() would then be chained on to the end of .then() to handle errors when a task cannot be completed.
// Here is an example.
const p3 = () => {
return new Promise((resolve,reject) => {
var condition = true;
if (condition === true) {
resolve('P3');
} else {
reject('Promise failed!');
}
});
};
p3('p3').then(resolve => {
console.log(resolve);
}).catch(reject => {
console.log(reject);
})
这是您的代码的同步等效项:
const f1 = () => {
console.log("F1");
};
const f2 = () => {
console.log("F2");
throw new Error();
};
const f3 = () => {
console.log("F3");
};
try {
f1();
} catch {
console.log("Caught f1");
}
try {
f2();
} catch {
console.log("Caught f2");
}
try {
f3();
} catch {
console.log("Caught f3");
}
console.log("Final code");
如您所见,这给出了匹配结果。希望在查看同步代码时,您不会对原因感到惊讶。在 try..catch
中,您可以尝试恢复。这个想法是 catch
将停止错误传播,您可以继续进行下去。或者,如果您确实想停止,您仍然必须再次明确throw
,例如:
doCode();
try {
makeCoffee();
} catch(err) {
if (err instanceof IAmATeapotError) {
//attempt recovery
makeTea();
} else {
//unrecoverable - log and re-throw
console.error("Fatal coffee related issue encountered",err);
throw err;
}
}
doCode();
这也是 Promise#catch()
服务的目的 - 因此您可以尝试恢复或至少在出现问题时采取行动。这个想法是, .catch()
之后,您也许可以继续:
const orderPizza = (topping) =>
new Promise((resolve,reject) => {
if (topping === "pepperoni")
reject(new Error("No pepperoni available"));
else
resolve(`${topping} pizza`);
});
const makeToast = () => "toast";
const eat = food => console.log(`eating some ${food}`);
async function main() {
await orderPizza("cheese")
.catch(makeToast)
.then(eat);
console.log("-----");
await orderPizza("pepperoni")
.catch(makeToast)
.then(eat);
}
main();
为了拒绝来自 .catch()
的承诺链,您需要做一些类似于正常的 catch
和 fail 在错误恢复时通过引发另一个错误。 You can throw
or return a rejected promise to that effect。
这段代码的工作原理完全一样。我看不出我的代码有什么不同,只是我单独声明了这些函数。
无论 Promise 在哪里被拒绝,上面的代码都会停止。
您显示的第二段代码在拒绝后完全失败,因为没有其他 .catch()
-es 成功。基本类似于这个同步代码:
try {
console.log("log #1");
throw new Error();
console.log("log #2");
console.log("log #3");
console.log("Final end");
} catch {
console.log("Caught");
}
因此,如果您不想早日恢复,您也可以跳过 .catch()
而不是引发另一个错误。
你没有做错任何事。
在您的代码中,您调用第一个承诺 p1。然后你写p1.catch(...).then(...).then(...).then(...)
。这是一个链,这意味着您应该调用 then
3 次,因为您在 p1 承诺中调用了 resolve 方法(所有这些 then
都依赖于第一个承诺)。
当 promise 被拒绝时,仍然会执行以下 .then
。
是的。准确地说:then
和 catch
方法调用都是同步执行的(一次性),因此所有涉及的承诺都是一次性创建的。传递给这些异步执行的方法的回调是相关的承诺解析(fullfill 或 reject)。
据我所知,当在承诺链中发生错误/拒绝时,不会再执行其后的 .then
调用。
事实并非如此。 catch
返回的承诺可以完全填充或拒绝取决于传递给它的回调中发生的情况,因此当该承诺解决时,链中更下方的回调将相应地执行。
拒绝被正确捕获,但为什么在捕获后记录“P3”?
在您的情况下,catch
回调返回 undefined
(它只执行 console.log
),它的承诺 fullfulls!结果,链式 then
回调——在 那个 承诺上——被执行......等等。
如果你想“停止”
如果你想保持链的原样,但希望有一个拒绝导致不再执行 then
或 catch
回调的行为,那么不要解析关联的承诺:
const stop = new Promise(resolve => null);
const p1 = () => {
return new Promise((resolve,reject) => {
console.log("P1");
resolve();
});
};
const p2 = () => {
return new Promise((resolve,reject) => {
console.log("P2");
reject();
});
};
const p3 = () => {
return new Promise((resolve,reject) => {
console.log("P3");
resolve();
});
};
p1().catch(() => {
console.log("Caught p1");
return stop; // don't resolve
}).then(p2).catch(() => {
console.log("Caught p2");
return stop;
}).then(p3).catch(() => {
console.log("Caught p3");
return stop;
}).then(() => {
console.log("Final then");
});