什么时候.then(成功,失败)被认为是承诺的反模式?

时间:2014-07-09 19:31:00

标签: javascript node.js promise bluebird

我查看了bluebird promise FAQ,其中提到.then(success, fail) is an antipattern。我不太了解它对try和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) })

有什么区别?

7 个答案:

答案 0 :(得分:183)

  

有什么区别?

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

这是控制流程图表:

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

第二个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和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()

答案 1 :(得分:32)

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

但是我相信你所链接的文本的意思是then对于链接一系列异步步骤的能力而言最有用,而当你实际执行此操作时,它的2参数形式由于上述原因,then巧妙地表现得并不像预期的那样。当使用中链时,它特别违反直觉。

作为一个做了很多复杂异步事情的人并且碰到了这样的角落,而不是我承认,我真的建议避免使用这种反模式并使用单独的处理程序方法。

答案 2 :(得分:17)

通过观察两者的优点和缺点,我们可以对哪种情况做出适当的猜测。 这是实施承诺的两种主要方法。两者都有它的优缺点

  

捕捉方法

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

<强>优点

  1. 所有错误都由一个catch块处理。
  2. 甚至在当时的阻止中捕获任何异常。
  3. 多次成功回调的链接
  4. <强>缺点

    1. 如果链接,则很难显示不同的错误消息。
    2.   

      成功/错误方法

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

      <强>优点

      1. 您可以获得精细的错误控制。
      2. 您可以针对各类错误(如db error,500 error等)设置常见的错误处理函数。
      3. <强> Disavantages

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

答案 3 :(得分:0)

简单的解释:

在ES2018中

  

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

     
      
  1. 让诺就是这个价值。
  2.   
  3. 返回?调用(承诺,“然后”,«未定义,onRejected»)。
  4.   

这意味着:

promise.then(f1).catch(f2)

等于

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

答案 4 :(得分:0)

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

答案 5 :(得分:0)

使用then()catch()有助于在诺言上链接成功和失败处理程序。catch()then()返回的诺言上起作用。它可以处理

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

enter image description here

  

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

这是有道理的,因为由then()返回的promise如果进行回调时不会出错。

答案 6 :(得分:-1)

而不是言语,好榜样。以下代码(如果第一个承诺已解决):

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