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

2022-08-30 00:58:58

我看了一下蓝鸟承诺常见问题解答,其中提到.then(成功,失败)是一个反模式。我不太明白它对和的解释。以下有什么问题?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) })

有什么区别?


答案 1

有什么区别?

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

下面是一个控制流程图:

control flow diagram of then with two argumentscontrol 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尝试的(推荐阅读!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() 来实现此目的。


答案 2

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

但我相信你链接到的文本的要点是,与回调相比,它最有用的是它能够链接一堆异步步骤,当你实际这样做时,由于上述原因,2参数形式的微妙表现并不像预期的那样。当在中链使用时,它特别违反直觉。thenthen

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