拒绝后承诺链继续
Posted
技术标签:
【中文标题】拒绝后承诺链继续【英文标题】:Promise chain continues after rejection 【发布时间】:2021-05-31 09:00:50 【问题描述】:我无法正确捕获承诺链中的错误/拒绝。
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
仍然会被执行。据我了解,当在 Promise 链中发生错误/拒绝时,随后的 .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在哪里被拒绝,上面的代码都会停止。
【问题讨论】:
拒绝后的第一个.catch()
负责处理错误,然后继续默认链。
@Sirko 这正是发生的事情。但是为什么?我认为链条在第一次拒绝时“中止”。如何在拒绝时停止执行以下功能?为什么我不能对所有承诺使用“全局”捕获?
您在从 p2 捕获错误后调用 p3。 catch 处理错误,然后像没有发生一样继续进行
catch
的返回值是一个承诺,所以它会有一个 then 方法对吗? developer.mozilla.org/en-US/docs/Web/javascript/Reference/…为什么你认为它应该停止?
您的假设不正确,仅此而已。 .catch 不会停止承诺链,原因与常规 try .. catch 不会停止位于 catch 之后的代码的代码执行相同。如果这样做就没有意义
【参考方案1】:
这是您的代码的同步等效项:
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()
,而不是引发另一个错误。
【讨论】:
【参考方案2】:试试这个。
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);
)
【讨论】:
【参考方案3】:你没有做错任何事。
在您的代码中,您调用第一个承诺 p1。然后你写p1.catch(...).then(...).then(...).then(...)
。这是一个链,意味着您应该调用 then
3 次,因为您在 p1 承诺中调用了 resolve 方法(所有这些 then
s 都依赖于第一个承诺)。
【讨论】:
这不是我的问题。如果我拒绝承诺,为什么链条不会“中止”。其余的仍在执行。我更新了我的问题,并提供了我的代码应该如何工作的工作示例。【参考方案4】:当 promise 被拒绝时,下面的
.then
仍然会被执行。
是的。准确地说:then
和 catch
方法调用都是同步执行的(一口气),因此所有涉及的 Promise 都是一次性创建的。传递给这些方法的 回调 是异步执行的,因为相关的 Promise 解决(完全填充或拒绝)。
据我了解,当在承诺链中发生错误/拒绝时,其后的
.then
调用将不再执行。
事实并非如此。 catch
返回的 Promise 可以根据传递给它的回调中发生的情况来填充或拒绝 ,因此当该 Promise 解决时,更下游的回调将相应地执行。
拒绝被正确捕获,但为什么在捕获后记录“P3”?
在您的情况下,catch
回调返回undefined
(它只执行console.log
),它的承诺fulls!结果,链式的then
回调——在 that promise 上——被执行......等等。
如果你想“停止”
如果你想保持链原样,但希望有一种行为,即拒绝导致不再执行 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");
);
【讨论】:
new Promise(resolve => null)
将停止整个承诺链。我不确定这是一个很好的主意,因为它使以后的任何恢复都变得不可能。即使是附加.finally()
之类的东西也不会被处理。如果不应该进行恢复尝试,我建议要么不附加.catch()
,因为知道承诺链失败通常比让它永远陷入困境更有价值。另一种方法是在.catch()
中通过抛出错误或返回被拒绝的承诺来拒绝。这仍然使链条的其余部分失败。
实际上它会停止整个承诺链。那是我对OP愿望的理解。我自己永远不会这样做,但我也不希望事情就这样停止。以上是关于拒绝后承诺链继续的主要内容,如果未能解决你的问题,请参考以下文章