【问题标题】:When is .then(success, fail) considered an antipattern for promises?什么时候 .the(success, fail) 被认为是 promise 的反模式?
【发布时间】:2014-08-30 23:41:22
【问题描述】:

我查看了bluebird promise FAQ,其中提到了.then(success, fail) is an antipattern。我不太明白它对trycatch 的解释。 以下有什么问题?

some_promise_call()
.then(function(res) { logger.log(res) }, function(err) { logger.log(err) })

似乎该示例表明以下是正确的方法。

some_promise_call()
.then(function(res) { logger.log(res) })
.catch(function(err) { logger.log(err) })

有什么区别?

【问题讨论】:

  • then().catch() 更具可读性,因为您无需查找逗号并调查此回调是否为成功或失败分支。
  • @KrzysztofSafjanowski - 被“看起来更好”的论点所摧毁。完全错误!
  • @AndreyPopov 你在哪里看到“看起来更好”?请阅读下一个答案以及更易读的.then(function(res) { logger.log(res) }, function(err) { logger.log(err) }).then(function(res) { logger.log(res) }).catch( function(err) { logger.log(err) })
  • 注意: 当您使用 .catch 时,您不知道是哪一步导致了问题 - 在最后一个 then 内或承诺链上的其他位置。所以它确实有它自己的缺点。
  • 我总是将函数名称添加到 promise .then() 参数中以使其可读,即some_promise_call() .then(function fulfilled(res) { logger.log(res) }, function rejected(err) { logger.log(err) })

标签: javascript node.js promise bluebird


【解决方案1】:

通过查看两者的优缺点,我们可以对哪种情况适合进行计算猜测。 这是实现 Promise 的两种主要方法。两者各有优劣

捕捉方法

some_promise_call()
.then(function(res) { logger.log(res) })
.catch(function(err) { logger.log(err) })

优势

  1. 所有错误都由一个 catch 块处理。
  2. 甚至在 then 块中捕获任何异常。
  3. 多个成功回调的链接

缺点

  1. 在链接的情况下,很难显示不同的错误消息。

成功/错误方法

some_promise_call()
.then(function success(res) { logger.log(res) },
      function error(err) { logger.log(err) })

优势

  1. 您可以获得细粒度的错误控制。
  2. 您可以对各种类型的错误(如 db 错误、500 错误等)使用通用错误处理功能。

缺点

  1. 如果您希望处理成功回调引发的错误,您仍然需要另一个 catch

【讨论】:

  • 对于只需要使用日志文件调试生产问题的人,我更喜欢成功/错误方法,因为它可以创建因果错误链,可以在应用程序的出口边界记录.
  • 问题。假设我进行了一个异步调用,它执行以下几件事之一:1)成功返回(2xx 状态码),2)返回不成功(4xx 或 5xx 代码)但本身没有被拒绝,3)或根本不返回(互联网连接中断)。对于案例 #1,.then 中的成功回调被命中。对于案例 #2,.then 中的错误回调被命中。对于案例 #3,调用 .catch。这是正确的分析,对吧?案例#2 是最棘手的,因为从技术上讲,4xx 或 5xx 不是拒绝,它仍然成功返回。因此,我们需要在 .then 中处理它。 ....我的理解正确吗?
  • "对于案例#2,.then 中的错误回调被命中。对于案例#3,.catch 被调用。这是正确的分析,对吧?" - 这就是 fetch 的工作原理
【解决方案2】:

有什么区别?

.then() 调用将返回一个承诺,如果回调抛出错误,该承诺将被拒绝。这意味着,当您的成功 logger 失败时,错误将被传递给以下 .catch() 回调,但不会传递给与 success 一起出现的 fail 回调。

这是一个控制流图:

用同步代码表示:

// some_promise_call().then(logger.log, logger.log)
then: {
    try {
        var results = some_call();
    } catch(e) {
        logger.log(e);
        break then;
    } // else
        logger.log(results);
}

第二个log(类似于.then()的第一个参数)只会在没有发生异常的情况下执行。标注的块和break的声明感觉有点奇怪,这其实是python has try-except-else for的意思(推荐阅读!)。

// some_promise_call().then(logger.log).catch(logger.log)
try {
    var results = some_call();
    logger.log(results);
} catch(e) {
    logger.log(e);
}

catch 记录器还将处理来自成功记录器调用的异常。

差别太大了。

我不太明白它对 try and catch 的解释

争论是,通常,您希望在处理的每个步骤中捕获错误,并且您不应该在链中使用它。期望您只有一个最终处理程序来处理所有错误 - 而当您使用“反模式”时,某些 then-callbacks 中的错误不会得到处理。

但是,这种模式实际上非常有用:当您想要处理恰好在这一步中发生的错误,并且您想要在没有发生错误的情况下完全不同 - 即当错误发生时不可恢复。 请注意,这是分支您的控制流。当然,有时这是需要的。


以下有什么问题?

some_promise_call()
.then(function(res) { logger.log(res) }, function(err) { logger.log(err) })

你不得不重复你的回调。你宁愿

some_promise_call()
   .catch(function(e) {
       return e; // it's OK, we'll just log it
   })
   .done(function(res) {
       logger.log(res);
   });

您也可以考虑为此使用.finally()

【讨论】:

  • 这是我这几天读到的最有帮助的解释(而且我读过很多)。我无法解释我有多感激! :) 我认为你应该更多地强调两者之间的区别,.catch即使在成功函数内部也能捕获错误.. 就我个人而言,我认为这是非常错误的,因为你最终会遇到一个错误-入口点,它可以从多个操作中得到多个错误,但这是我的问题。无论如何-感谢您的信息!难道你没有一些你愿意分享的在线交流工具,所以我可以再问一些事情吗? :P
  • 我希望this 在这里给你更多的支持。绝对是本网站上对重要 Promise 机制的最佳解释之一。
  • .done() 不是标准的一部分,是吗?至少 MDN 没有列出该方法。这会很有帮助。
  • @ygoe 确实。 done 是一个 Bluebird 的东西,基本上被 then+unhandled-rejection detection 弃用了。
  • 只是一个色盲的笔记:这些图表毫无意义:)
【解决方案3】:

使用then()catch() 有助于在promise 上链接成功和失败处理程序。catch() 适用于then() 返回的promise。它处理,

  1. 如果承诺被拒绝。参见图片中的#3
  2. 如果 then() 的成功处理程序中发生错误,则在下面的第 4 到第 7 行之间。参见图片中的#2.a (then() 上的失败回调无法处理此问题。)
  3. 如果 then() 的失败处理程序中发生错误,则在下面的第 8 行。参见图片中的#3.b。

1. let promiseRef: Promise = this. aTimetakingTask (false); 2. promiseRef 3. .then( 4. (result) => { 5. /* successfully, resolved promise. 6. Work on data here */ 7. }, 8. (error) => console.log(error) 9. ) 10. .catch( (e) => { 11. /* successfully, resolved promise. 12. Work on data here */ 13. });

注意:很多时候,如果catch() 是 已经写好了。 编辑:reject() 导致调用catch() 仅当错误 then() 中的处理程序定义。注意图片中的#3 catch()。当第 8 行和第 9 行中的处理程序不存在时调用它 定义。

这是有道理的,因为如果由回调处理,then() 返回的承诺不会出错。

【讨论】:

  • 从数字 3 到 catch 回调的箭头似乎是错误的。
  • 谢谢!使用 then() 中定义的错误回调,它不会被调用(代码 sn-p 中的第 8 行和第 9 行)。 #3 调用两个箭头之一。这是有道理的,因为 then() 返回的 Promise 没有错误,如果回调正在处理它。编辑了答案!
【解决方案4】:

使用.then().catch() 可让您启用完成工作流所需的Promise Chaining。您可能需要从数据库中读取一些信息,然后您想将其传递给异步 API,然后您想操作响应。您可能希望将响应推送回数据库。用您的概念处理所有这些工作流程是可行的,但很难管理。更好的解决方案是then().then().then().then().catch(),它在一次捕获中接收所有错误并让您保持代码的可维护性

【讨论】:

    【解决方案5】:

    简单解释:

    在 ES2018 中

    当使用参数 onRejected 调用 catch 方法时, 采取以下步骤:

    1. 让 promise 成为 this 值。
    2. 返回? Invoke(promise, "then", « undefined, onRejected »).

    意思是:

    promise.then(f1).catch(f2)
    

    等于

    promise.then(f1).then(undefiend, f2)
    

    【讨论】:

      【解决方案6】:

      代替文字,很好的例子。以下代码(如果第一个承诺已解决):

      Promise.resolve()
      .then
      (
        () => { throw new Error('Error occurs'); },
        err => console.log('This error is caught:', err)
      );
      

      等同于:

      Promise.resolve()
      .catch
      (
        err => console.log('This error is caught:', err)
      )
      .then
      (
        () => { throw new Error('Error occurs'); }
      )
      

      但是在第一个 promise 被拒绝的情况下,这并不相同:

      Promise.reject()
      .then
      (
        () => { throw new Error('Error occurs'); },
        err => console.log('This error is caught:', err)
      );
      
      Promise.reject()
      .catch
      (
        err => console.log('This error is caught:', err)
      )
      .then
      (
        () => { throw new Error('Error occurs'); }
      )
      

      【讨论】:

      • 这没有意义,你能删除这个答案吗?这会误导并分散正确答案的注意力。
      • @AndyRay,这在实际应用中没有意义,但理解 Promise 的工作是有意义的。
      • 我认为这段代码确实需要一些词,这样我们才能理解它试图告诉我们什么。它们如何相同,又如何不同?
      【解决方案7】:

      两者并不完全相同。不同之处在于第一个示例不会捕获success 处理程序中引发的异常。因此,如果您的方法应该只返回已解决的承诺,通常情况下,您需要一个尾随的 catch 处理程序(或另一个带有空 success 参数的 then)。当然,您的 then 处理程序可能没有执行任何可能失败的操作,在这种情况下,使用一个 2 参数 then 可能没问题。

      但我相信您链接到的文本的重点是 then 在链接一堆异步步骤的能力方面与回调相比最有用,当您实际执行此操作时,@987654328 的 2 参数形式由于上述原因,@subtly 的行为并不像预期的那样。在中链使用时尤其违反直觉。

      作为一个做过很多复杂异步工作的人,遇到这样的问题比我愿意承认的还要多,我真的建议避免这种反模式并使用单独的处理程序方法。

      【讨论】:

        猜你喜欢
        • 2020-07-16
        • 1970-01-01
        • 2012-09-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-02
        相关资源
        最近更新 更多