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

When is .then(success, fail) considered an antipattern for promises?

提问人:user2127480 提问时间:7/10/2014 最后编辑:Sabito stands with Ukraineuser2127480 更新时间:12/29/2021 访问量:36079

问:

我看了一下 bluebird promise FAQ,其中提到 .then(success, fail) 是一个反模式。我不太明白它对 和 的解释。 以下有什么问题?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) })

有什么区别?

JavaScript 节点.js 承诺 蓝鸟

评论

4赞 Krzysztof Safjanowski 7/10/2014
then().catch()更具可读性,因为您不需要查找逗号并调查此回调是成功还是失败分支。
15赞 Andrey Popov 2/27/2015
@KrzysztofSafjanowski - 被“看起来更好”的论点所摧毁。完全错了!
1赞 Krzysztof Safjanowski 3/1/2015
@AndreyPopov你在哪里看到“看起来更好”?.请阅读下一个答案,哪个更具可读性或.then(function(res) { logger.log(res) }, function(err) { logger.log(err) }).then(function(res) { logger.log(res) }).catch( function(err) { logger.log(err) })
11赞 vitaly-t 10/25/2015
注意:当您使用 时,您不知道是哪个步骤导致了问题 - 在最后一个步骤或承诺链的其他地方。所以它确实有其自身的缺点。.catchthen
2赞 Shane Rowatt 7/7/2016
我总是在 promise .then() 参数中添加函数名称以使其可读,即some_promise_call() .then(function fulfilled(res) { logger.log(res) }, function rejected(err) { logger.log(err) })

答:

44赞 acjay 7/10/2014 #1

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

但我相信你链接到的文本的要点是,与回调相比,它最有用的是它能够链接一堆异步步骤,当你实际这样做时,由于上述原因,微妙的 2 参数形式的行为并不完全符合预期。当在链条中间使用时,这尤其违反直觉。thenthen

作为一个做过很多复杂的异步工作并且遇到过这样的角落的人,我真的建议避免这种反模式,并使用单独的处理程序方法。

260赞 Bergi 7/10/2014 #2

有什么区别?

该调用将返回一个承诺,如果回调引发错误,该承诺将被拒绝。这意味着,当您的成功失败时,错误将传递给下一个回调,但不会传递给旁边的回调。.then()logger.catch()failsuccess

下面是一个控制流程图:

control flow diagram of then with two arguments control flow diagram of then catch chain

要用同步代码表示它,请执行以下操作:

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

第二个(类似于第一个参数 ),只有在没有发生异常的情况下才会执行。标记的块和语句感觉有点奇怪,这实际上是 python 的 try-except-else 用途(推荐阅读!log.then()break

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

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

差异就这么多。

我不太明白它对尝试和捕获的解释

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

但是,此模式实际上非常有用:当您想要处理在此步骤中发生的错误时,并且希望在没有发生错误时(即当错误不可恢复时)执行完全不同的事情时。请注意,这是对控制流的分支。当然,这有时是需要的。


以下有什么问题?

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

评论

12赞 Andrey Popov 2/27/2015
这是我几天来读到的最有用的解释(我读了很多)。我无法解释我有多感激!:)我认为你应该更多地强调两者之间的区别,即使在成功函数中也会发现错误。就我个人而言,我发现这是非常错误的,因为你最终会得到一个错误入口点,它可以从多个操作中获得多个错误,但这是我的问题。无论如何 - 谢谢你的信息!难道你没有一些你愿意分享的在线交流工具,这样我就可以多问一些事情了吗?:P.catch
2赞 Patrick Roberts 2/3/2018
我希望能给你更多的赞成票。绝对是本网站上对重要机制的最佳解释之一。Promise
2赞 ygoe 4/1/2019
.done()不是标准的一部分,是吗?至少 MDN 没有列出该方法。这将是有帮助的。
1赞 Bergi 4/1/2019
@ygoe确实如此。done 是 Bluebird 的东西,基本上被 +unhandled-reject 检测弃用了。then
3赞 Benny K 11/22/2019
只是色盲的一句话:这些图表毫无意义:)
20赞 aWebDeveloper 3/20/2016 #3

通过查看两者的优缺点,我们可以对哪种情况进行有计划的猜测。 这是实现承诺的两种主要方法。两者都有它的优点和缺点

捕获方法

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. 您可以对各种类别的错误(如数据库错误,500错误等)具有常见的错误处理功能。

Disavantages

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

评论

0赞 Shane Rowatt 7/7/2016
对于需要仅使用日志文件调试生产问题的人,我更喜欢成功/错误方法,因为它能够创建可以在应用程序的出口边界处记录的因果错误链。
0赞 Benjamin Hoffman 8/30/2017
问题。假设我做了一个异步调用,它做了以下几件事之一:1)成功返回(2xx状态代码),2)返回失败(4xx或5xx代码),但本身没有被拒绝,3)或根本没有返回(互联网连接关闭)。对于案例 #1,命中 .then 中的成功回调。对于案例 #2,命中 .then 中的错误回调。对于案例 #3,调用 .catch。这是正确的分析,对吧?案例 #2 是最棘手的,从技术上讲,4xx 或 5xx 不是拒绝,它仍然成功返回。因此,我们需要在 .then 中处理它。....我的理解正确吗?
0赞 aWebDeveloper 9/4/2017
“对于案例 #2,命中了 .then 中的错误回调。对于案例 #3,调用 .catch。这是正确的分析,对吧? - 这就是 fetch 的工作原理
-2赞 ktretyak 12/29/2017 #4

代替言语,好榜样。以下代码(如果第一个 promise 已解决):

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.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'); }
)

评论

4赞 Andy Ray 5/15/2018
这没有意义,你能删除这个答案吗?这是误导性的,会分散对正确答案的注意力。
0赞 ktretyak 5/15/2018
@AndyRay,这在实际应用中没有意义,但理解承诺的工作是有意义的。
0赞 Ruan Mendes 7/27/2021
我认为这段代码确实需要一些文字,这样我们才能理解它试图告诉我们什么。它们如何相同,又如何不相同?
2赞 Shuai Li 11/17/2018 #5

简单解释:

在 ES2018 中

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

  1. 让 promise 成为这个值。
  2. 返回?Invoke(promise, “then”, « undefined, onRejected »)。

这意味着:

promise.then(f1).catch(f2)

等于

promise.then(f1).then(undefiend, f2)
2赞 Jayant Varshney 5/3/2019 #6

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

1赞 Venkey 9/16/2019 #7

使用 和 帮助链上的成功和失败处理程序上的承诺。对 返回的 promise 起作用。它处理,then()catch()catch()then()

  1. 如果承诺被拒绝。见图中的#3
  2. 如果在 then() 的成功处理程序中发生错误,则在下面的第 4 行到第 7 行之间。见图中的#2.a (失败回调 on 不处理此问题。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. });

enter image description here

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

这是有道理的,因为如果回调正在处理它,则返回的 promise 不会出错。then()

评论

0赞 Bergi 9/16/2019
从数字 3 到回调的箭头似乎是错误的。catch
0赞 Venkey 9/16/2019
谢谢!在 then() 中定义错误回调时,不会调用它(代码片段中的行 #8 和 #9)。#3 调用两个箭头之一。这是有道理的,因为如果回调正在处理它,then() 返回的 promise 不会出错。编辑了答案!