【问题标题】:Placement of catch BEFORE and AFTER then之前和之后的捕获位置
【发布时间】:2017-06-20 04:00:38
【问题描述】:

我很难理解将 .catch BEFORE 和 AFTER then 放入嵌套承诺之间的区别。

备选方案 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,test1 失败如果数字是> 10,test2 失败,如果数字不是100,test3 失败。在这种情况下 test2 只是失败了。

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

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

这个问题的出现是因为我之前发布的帖子How to turn nested callback into promise?。我认为这是一个不同的问题,值得发布另一个主题。

【问题讨论】:

  • .then 和 .catch 都可以改变承诺......所以我不确定误解来自哪里。如果你把 catch 放在 .then 之前,它会捕获在 .then 之前发生的拒绝,.then 将根据 .catch 中发生的情况运行它的完成/失败回调,当你交换它们时反之亦然。跨度>
  • 对不起,如果我的问题不清楚。但在这种情况下,正如我所说,两种情况的行为相同,所以我看不出区别。你能告诉我我们什么时候把它放在之前,什么时候我们决定把它放在之后?把它放在后面似乎真的很直观和常见。只是不知道为什么有时我们把它放在那之前
  • 如果他们的表现相同,那只是因为在这种特定情况下,他们所做的并不会改变结果。任何一个的微小变化都可能改变结果。
  • “改变结果”是什么意思。对不起,我真的很困惑哈哈
  • 例如,如果你什么都不做而不是抛出错误,promise 将从被拒绝切换到被解决。这当然会改变结果,因为这个承诺现在是一个已解决的承诺,而不是一个被拒绝的承诺。 (当然除非它已经解决了,在这种情况下,catch 无论如何都不会运行)

标签: javascript node.js promise bluebird


【解决方案1】:

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

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

和

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

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

p 解析后会发生什么:

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

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

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

p 拒绝时会发生什么:

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

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

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

何时使用:

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

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

其他选项

还有另一个选项可以同时使用这两个回调,您可以将其传递给.then(),如下所示:

 p.then(fn1, fn2)

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

【讨论】:

  • 这个问题具体是关于.then()和.catch()的顺序,你回答。此外,您还提供了一些关于何时使用哪个顺序的提示,我认为在此处提及第三个选项是合适的,即将成功和错误处理程序都传递给.then()。在这种情况下,最多会调用一个处理程序。
  • @ArneHugo - 好建议。我加了。
  • 那么,在 Promise Chaining 期间,我们可以编写 .then .catch .catch .then 类的场景吗?
  • @KapilRaghuwanshi,是的,您可以使用它在失败的情况下传递默认值。即Promise.reject(new Error("F")).then(x => x).catch(e => {console.log(e); return [1]}).then(console.log)和Promise.resolve([2]).then(x => x).catch(e => [1]).then(console.log)
  • @DmitryShvedov - 如我所料,这是错误的.then(this.setState({isModalOpen: false}))。您没有传递对.then() 的函数引用,因此括号中的代码会立即执行(在promise 解决之前)。应该是.then(() => this.setState({isModalOpen: false}))。
【解决方案2】:

jfriend00's answer 很好,但我认为添加类似的同步代码是个好主意。

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()。

【讨论】:

  • 这是很好的解释,但您可以将孤儿 then() 包装在 finally{...} 中
  • @82Tuskers,你确定吗?如果我将then() 放入finally{...} 中,即使handleCatch() 抛出,它不会被错误地调用吗?请记住,我的目标是展示类似的同步代码,而不是建议处理异常的不同方式
  • 所以,如果我们想处理所有情况但仍然链接 .then() 最好使用 .then(do something).catch(log err and update state).then(do another thing).catch(log err) 我们尝试在每个点捕获但还继续执行 stmnts 的地方?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多