捕获之前和之后的位置

Placement of catch BEFORE and AFTER then

我无法理解将 .catch 放在嵌套承诺中之前和之后的区别。

备选方案 1:

test1Async(10).then((res) => {
  return test2Async(22)
    .then((res) => {
      return test3Async(100);
    }).catch((err) => {
      throw "ERROR AFTER THEN";
    });
}).then((res) => {
  console.log(res);
}).catch((err) => {
  console.log(err);
});

备选方案 2:

test1Async(10).then((res) => {
   return test2Async(22)
     .catch((err) => {
        throw "ERROR BEFORE THEN";
      })
      .then((res) => {
        return test3Async(100);
      });
  }).then((res) => {
    console.log(res);
  }).catch((err) => {
    console.log(err);
  });

每个函数的行为如下,如果数字为 <0,则测试 1 失败;如果数字为 > 10,则测试 2 失败;如果数字不是 100,则测试 3 失败。在这种情况下,test2 只是失败了。

我尝试 运行 并使 test2Async 失败,然后 BEFORE 和 AFTER 的行为方式相同并且不执行 test3Async。有人可以向我解释将 catch 放置在不同位置的主要区别吗?

在每个函数中我console.log('Running test X')为了检查它是否被执行。

这个问题的出现是因为我之前发布的帖子 。我认为这是一个不同的问题,值得发布另一个主题。

所以,基本上你是在问这两者之间的区别是什么(其中 p 是从以前的一些代码创建的承诺):

return p.then(...).catch(...);

return p.catch(...).then(...);

当 p 解析或拒绝时存在差异,但这些差异是否重要取决于 .then().catch() 处理程序中的代码。

p 结算时会发生什么:

在第一个方案中,当p 解析时,调用.then() 处理程序。如果 .then() 处理程序 return 是一个值或另一个最终解决的承诺,那么 .catch() 处理程序将被跳过。但是,如果 .then() 处理程序抛出或 return 一个最终拒绝的承诺,那么 .catch() 处理程序将执行原始承诺 p 中的拒绝,但也会.then() 处理程序中发生的错误。

在第二种方案中,当p解析时,调用.then()处理程序。如果该 .then() 处理程序抛出或 return 最终拒绝的承诺,则 .catch() 处理程序无法捕获它,因为它在链中位于它之前。

所以,这就是差异#1。如果 .catch() 处理程序是 AFTER,那么它也可以捕获 .then() 处理程序内部的错误。

p 拒绝时会发生什么:

现在,在第一个方案中,如果 promise p 被拒绝,则 .then() 处理程序将被跳过,.catch() 处理程序将按您预期的那样被调用。您在 .catch() 处理程序中所做的决定了最终结果是 return。如果您只是 return 来自 .catch() 处理程序的值或 return 最终解决的承诺,那么承诺链会切换到已解决状态,因为您“处理”了错误并且 return正常。如果您在 .catch() 处理程序中抛出或 return 一个被拒绝的承诺,那么 returned 承诺将保持被拒绝状态。

在第二种方案中,如果承诺 p 拒绝,则调用 .catch() 处理程序。如果您 return 一个正常值或最终从 .catch() 处理程序解决的承诺(因此“处理”错误),则承诺链切换到已解决状态和 .then() 处理程序.catch() 之后会被调用。

这就是差异#2。如果 .catch() 处理程序是 BEFORE,那么它可以处理错误并允许 .then() 处理程序仍然被调用。

何时使用哪个:

如果您只需要一个 .catch() 处理程序可以捕获原始承诺 p.then() 处理程序中的错误以及来自 [=13] 的拒绝,请使用第一个方案=] 应该跳过 .then() 处理程序。

如果您希望能够捕获原始承诺中的错误,请使用第二种方案 p 并且可能(取决于条件)允许承诺链继续解决,从而执行 .then()处理程序。

另一个选项

还有一个选项可以使用两个回调,您可以将其传递给 .then(),如:

 p.then(fn1, fn2)

这保证只会调用 fn1fn2 之一。如果 p 解析,那么 fn1 将被调用。如果 p 拒绝,则 fn2 将被调用。 fn1 中的任何结果变化都不会使 fn2 被跟注,反之亦然。所以,如果你想绝对确保只调用你的两个处理程序中的一个,而不管处理程序本身发生了什么,那么你可以使用 p.then(fn1, fn2).

非常好,但我认为添加类似的同步代码是个好主意。

return p.then(...).catch(...);

类似于同步:

try {
  iMightThrow() // like `p`
  then()
} catch (err) {
  handleCatch()
}

如果iMightThrow()没有抛出,then()将被调用。如果它确实抛出(或者如果 then() 本身抛出),那么 handleCatch() 将被调用。请注意 catch 块如何无法控制 then 是否被调用。

另一方面,

return p.catch(...).then(...);

类似于同步:

try {
  iMightThrow()
} catch (err) {
  handleCatch()
}

then()

在这种情况下,如果 iMightThrow() 没有抛出,那么 then() 就会执行。如果确实抛出,则由 handleCatch() 决定是否调用 then(),因为如果 handleCatch() 重新抛出,则不会调用 then(),因为异常将立即抛给调用者。如果 handleCatch() 可以优雅地处理问题,那么 then() 将被调用。