什么时候 .then(success, fail) 被认为是 promise 的反模式?

IT技术 javascript node.js promise bluebird
2021-01-10 19:15:05

我查看了bluebird promise FAQ,其中提到这.then(success, fail)是一个反模式我不太明白它对tryand 的解释catch以下有什么问题?

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) })

有什么不同?

6个回答

有什么不同?

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

这里有一个控制流图:

带有两个参数的 then 的控制流程图 then catch链的控制流程图

用同步代码表示:

// 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所拥有try-except-else(推荐阅读!)。

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

catch记录仪也将处理来自成功记录通话例外。

区别就这么多。

我不太明白它对 try 和 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()为此使用

只是一个色盲的笔记:图表没有意义:)
2021-03-10 19:15:05
这是我这几天读到最有用的解释(而且我读了很多)。我无法解释我有多感激!:) 我认为你应该更多地强调两者之间的区别,即使在成功函数内部.catch也会发现错误.. 就我个人而言,我觉得这是非常错误的,因为你最终会得到一个错误入口点,它可以从中得到多个错误多个操作,但这是我的问题。无论如何 - 感谢您的信息!难道您没有一些愿意分享的在线交流工具,以便我可以多问几件事吗?:P
2021-03-30 19:15:05
我希望会给你更多的支持。绝对是Promise对本网站重要机械师的最佳解释之一
2021-04-01 19:15:05
.done()不是标准的一部分,是吗?至少 MDN 没有列出那个方法。这会很有帮助。
2021-04-08 19:15:05
@ygoe 确实如此。done是 Bluebird 的东西,基本上被then+unhandled-rejection 检测弃用了
2021-04-08 19:15:05

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

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

作为一个已经完成了很多复杂的异步工作并且遇到这样的角落的人,我真的不想承认,我真的建议避免这种反模式并使用单独的处理程序方法。

通过查看两者的优缺点,我们可以对哪种情况适合这种情况进行计算猜测。这是实现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 是最棘手的 bc 从技术上讲 4xx 或 5xx 不是拒绝,它仍然成功返回。因此,我们需要在 .then 中处理它。....我的理解正确吗?
2021-03-18 19:15:05
“对于案例#2,.then 中的错误回调被命中。对于案例#3,.catch 被调用。这是正确的分析,对吧?” - 这就是 fetch 的工作原理
2021-03-25 19:15:05
对于只需要使用日志文件调试生产问题的人,我更喜欢成功/错误方法,因为它能够创建一个因果错误链,可以在应用程序的退出边界处记录。
2021-04-07 19:15:05

简单说明:

在 ES2018

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

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

这意味着:

promise.then(f1).catch(f2)

等于

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

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