JavaScript Promise:拒绝处理程序 vs 捕获处理程序

5
我曾经遇到过许多应用程序中,使用 catch 要比 rejectHandler 更受青睐。 例如: 更倾向于使用 try...catch
new Promise.then(resolveHandler).catch()

替代

new Promise().then(resolveHandler, rejectHandler).catch()

这个有特别的原因吗? 我觉得
new Promise().then(resolveHandler, rejectHandler).catch()

为了更加有用,因为

  1. 我可以使用rejectHandler来解决设计/预期的错误场景,其中调用Promise.reject。
  2. 我可以使用catch块来处理发生的未知/意外编程/运行时错误。

有人知道为什么不经常使用rejectHandler吗?

P.S. 我知道ES6中有更新的替代方法,但我只是想知道这个问题。

更新:我知道rejectHandler和catch如何工作。问题是为什么我看到更多的人仅使用catch而不是同时使用rejectHandler和catch?这是最佳实践还是存在某些优势?

更新(在此添加答案):找到了我一直在寻找的答案。 原因不仅是出于catch中处理拒绝的错误,主要是由于链接。 当我们链接promise.then.then.then.then时,具有resolve,reject模式会有些棘手,因为您不希望实现rejecthandler来将rejectData转发到上面的链。 仅使用promise / then / catch以及resolve / return / throw在链接N个thenables时非常有用。 @Bob-Fanger(接受的答案)也解决了部分问题。 例如:

getData(id) {
        return service.getData().then(dataList => {
            const data = dataList.find(data => {
                return data.id === id;
            });
            if (!data) {
                // If I use Promise.reject here and use a reject handler in the parent then the parent might just be using the handler to route the error upwards in the chain
              //If I use Promise.reject here and parent doesn't use reject handler then it goes to catch which can be just achieved using throw.
                throw {
                    code: 404,
                    message: 'Data not present for this ID'
                };
            }
            return configuration;
        });
    }


//somewhere up the chain
....getConfiguration()
            .then(() => {
                //successful promise execution
            })
            .catch(err => {
                if (err.code) {
                    // checked exception
                    send(err);
                } else {
                    //unchecked exception
                    send({
                        code: 500,
                        message: `Internal Server error: ${err}`
                    });
                }
            });

只用这些,我只需担心 promise/then/catch 以及链中的 resolve/return/throw。


1
这是最佳实践,还是有一些优势吗?只编写一个函数而不是两个,我认为目前并没有确立最佳实践。我认为人们只是觉得这样更容易理解。 - Felix Kling
1
这是最佳实践还是有一些优势?没有任何技术优势(因此也没有最佳实践)。正如我在回答中所说,这真的取决于您需要代码实现什么。 - Adam Jenkins
P.S. 我知道 ES6 中有更新的替代方案,但我只是好奇想知道这个。-- 有哪些更新的替代方案? - Adam Jenkins
1
就我个人而言,我曾在符合要求的单个场合使用过 promise.then(resolveHandler, rejectHandler).catch(catchHandler) 这种“模式”。然后我意识到需要重新审视需求,因此不再使用该模式。 - Jaromanda X
我相当确定这里有几个重复的,因为这肯定已经被问过了。我会尝试找到它们。主要区别在于.catch()处理程序还将捕获resolveHandler内部的任何异常或拒绝,但rejectHandler不会。 - jfriend00
显示剩余3条评论
3个回答

7

区别在于,如果在resolveHandler内部发生错误,则不会由rejectHandler处理,后者仅处理原始promise中的拒绝。

rejectHandler与catch结合使用的情况不多,因为大多数情况下我们只关心某些事情出了问题。
创建一个错误处理程序可以使代码更易于理解。

如果链中的特定promise应该以不同的方式处理,那么可以使用rejectHandler,但在这种情况下,我可能会编写catch().then().catch()


2
有趣的解释。 另外,关于你提到的“rejectHandler在很少与catch一起使用,因为大多数时候我们只关心出了什么问题。”这一点是真的,虽然我随着时间的推移意识到考虑它是一个预期的错误还是一个意外的错误也是很好的。 - wallop

6

这两者都同样有用。当发生错误或Promise被拒绝时,拒绝处理程序和catch回调都会被调用。

没有使用其中一个的“最佳实践”。您可能会看到代码使用其中一个,但其使用将基于代码需要实现的内容。程序员可能希望在链中的不同时间捕获错误,并以不同的方式处理抛出的错误。

希望以下内容能够帮助解释我的意思:

somePromise
  .then(
      function() { /* code when somePromise has resolved */ },
      function() { 
        /* code when somePromise has thrown or has been rejected. 
        An error thrown in the resolvedHandler 
        will NOT be handled by this callback */ }
   );

somePromise
  .then(
      function() { /* code when somePromise has resolved */ }
   )
   .catch(
      function() { 
        /* code when somePromise has thrown or has been rejected OR 
        when whatever has occurred in the .then 
        chained to somePromise has thrown or 
        the promise returned from it has been rejected */ }
   );

请注意,在第一个代码片段中,如果已解决的处理程序抛出错误,则没有拒绝的处理程序(或catch回调)可以捕获该错误。在已解析的回调中抛出的错误将不会被指定为第二个参数的rejectedHandler捕获.then


谢谢。没有使用其中一个的“最佳实践”。您可能会看到代码使用其中一个,但其使用将基于代码需要实现的内容。 - wallop

0

如帖子中所述,在同一次调用.then时提供解析和拒绝处理程序,允许单独处理前一个承诺的拒绝,而不是在成功处理程序中抛出的错误或返回拒绝的承诺。

因为拒绝处理程序返回而没有抛出错误会恢复承诺链的完成通道,如果前一个拒绝处理程序正常返回,则最终的catch处理程序将不会被调用。

问题随后转化为使用情况、开发成本和知识水平。

使用情况

理论上,可以使用then调用的两个参数形式来重试操作。但由于硬编码的承诺链是静态设置的,因此重新尝试操作并不简单。更容易的重试方法可能是使用一个async函数,其中包含try-catch语句,围绕可能需要重试的承诺的await,就像这个概念代码中所示:

async function() {
    let retries = 3;
    let failureErr = null;
    while( retries--) {
       try {
          var result = await retryableOperationPromise() 
          return result;
       }
       catch( err) {
          failureErr = err;
       }
     }
     throw failureErr // no more retries
}

其他用例可能不是很普遍。

开发成本或商业决策。

如果告诉用户稍后重试是可以接受的,那么这可能比针对承诺拒绝的具体原因采取任何措施更便宜。例如,如果我在午夜尝试预订航班时,航空公司会提高价格,我通常会被告知“发生错误,请稍后再试”,因为我在预订开始时得到的价格将不被承认。

知识(<意见>)

我怀疑承诺的使用可能经常基于示例而不是深入的主题知识。程序经理可能希望尽可能简化代码库,以适应经验较少的开发人员(可能是成本问题)。

如果使用有效,则“最佳实践”可能并不真正适用于决定如何使用承诺。一些开发人员和经理会避免某些形式的使用,但并不总是基于技术优势。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接