重新抛出 promise catch 中的错误

Posted

技术标签:

【中文标题】重新抛出 promise catch 中的错误【英文标题】:Rethrowing error in promise catch 【发布时间】:2017-06-29 06:25:05 【问题描述】:

我在教程中找到了以下代码:

promise.then(function(result)
    //some code
).catch(function(error) 
    throw(error);
);

我有点困惑:catch 调用有什么作用吗?在我看来,它没有任何效果,因为它只是抛出了被捕获的相同错误。我基于常规 try/catch 的工作原理。

【问题讨论】:

您能提供教程的链接吗?也许还有其他有用的上下文... @Igor 我不能,它在 Pluralsight 上。这可能只是一些错误处理逻辑的占位符吗? 这就是我的猜测,因为它只会将错误传递给调用者,这也可以通过没有捕获开始来完成。 @TylerDurden 我怀疑您对它作为占位符的看法是正确的。 @TylerDurden,我也猜它是一个占位符。也许试图演示如何格式化/规范化错误。基本上相当于try ... catch(error) throw new Error("something went wrong") 的承诺。或者表明 Promises 和 Errors 是兼容的(至少是这样)。但在目前的实现中,它只是愚蠢的。你是对的,它什么都不做,甚至不像你在 OOP 中添加的一个钩子,以便在继承类中覆盖它。我会在它做某事后立即添加 catch-block,但不是那样,不仅仅是作为占位符。 【参考方案1】:

正如你所展示的那样,赤裸裸的接球和投掷是没有意义的。除了添加代码和降低执行速度之外,它没有做任何有用的事情。所以,如果你要去.catch() 并重新抛出,你应该在.catch() 中做一些事情,否则你应该完全删除.catch()

该通用结构的通常点是当您想要在.catch() 中执行某些操作时,例如记录错误或清理某些状态(例如关闭文件),但您希望承诺链继续被拒绝。

promise.then(function(result)
    //some code
).catch(function(error) 
    // log and rethrow 
    console.log(error);
    throw error;
);

在教程中,它可能只是为了向人们展示他们可以在哪里捕获错误或教授处理错误然后重新抛出错误的概念。


捕捉和重新抛出的一些有用的原因如下:

    您想记录错误,但将承诺链保留为被拒绝。 您希望将错误转化为其他错误(通常是为了在链的末端更容易处理错误)。在这种情况下,您将重新引发不同的错误。 您希望在 Promise 链继续之前进行大量处理(例如关闭/释放资源),但您希望 Promise 链保持被拒绝状态。 如果出现故障,您希望在 Promise 链中的这一点为调试器放置断点。 您希望处理特定错误或一组错误,但重新抛出其他错误,以便它们传播回调用者。

但是,在 catch 处理程序中没有其他代码的情况下对同一错误进行简单的捕获和重新抛出对于代码的正常运行没有任何帮助。

【讨论】:

在我看来,这不是一个很好的例子。使用这种方法,您可以轻松获得 1 个错误的多个日志记录。在java中你可以throw new Exception(periousException);我不知道javascript是否支持嵌套错误,但无论如何“记录并抛出”是不好的做法。 @Cherry - 你不能说这是一个不好的做法。有时模块想要以自己的方式记录自己的错误,这是一种方法。此外,我不建议这样做,我只是在解释没有理由拥有 .catch() 并在 catch 中抛出相同的错误,除非您在 .catch() 中执行其他操作。这就是这个答案的重点。 一般情况下异常应该符合抽象级别。例如,捕获与数据库相关的异常并抛出类似“服务”异常的东西是完全可以的,该异常将由调用者处理。当您不想公开有关低级异常的详细信息时,这尤其有用 捕获和(有时)抛出的另一个很好的理由是处理特定错误,但重新抛出其他所有错误。 @SimonZyx - 是的,.finally() 对此非常有用,但有时资源已经在非错误路径中得到处理,因此.catch() 仍然是关闭它们的地方。这真的取决于情况。【参考方案2】:

.then().catch() 方法都返回 Promises,如果您在任一处理程序中抛出异常,返回的承诺将被拒绝,并且该异常将在下一个拒绝处理程序中被捕获。

在下面的代码中,我们在第一个.catch() 中抛出一个异常,该异常在第二个.catch() 中被捕获:

new Promise((resolve, reject) => 
    console.log('Initial');

    resolve();
)
.then(() => 
    throw new Error('Something failed');
        
    console.log('Do this'); // Never reached
)
.catch(() => 
    console.log('Something failed');
    throw new Error('Something failed again');
)
.catch((error) => 
    console.log('Final error : ', error.message);
);

第二个.catch()返回一个已实现的Promised,可以调用.then()处理程序:

new Promise((resolve, reject) => 
    console.log('Initial');

    resolve();
)
.then(() => 
    throw new Error('Something failed');
        
    console.log('Do this'); // Never reached
)
.catch(() => 
    console.log('Something failed');
    throw new Error('Something failed again');
)
.catch((error) => 
    console.log('Final error : ', error.message);
)
.then(() => 
    console.log('Show this message whatever happened before');
);

有用的参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#Chaining_after_a_catch

希望这会有所帮助!

【讨论】:

【参考方案3】:

如果您完全省略 catch 方法调用,则没有重要区别。

它唯一增加的是一个额外的微任务,这实际上意味着你会发现承诺被拒绝的时间要晚于没有catch 子句的承诺失败。

下一个 sn-p 演示了这一点:

var p;
// Case 1: with catch
p = Promise.reject('my error 1')
       .catch(function(error) 
          throw(error);
       );

p.catch( error => console.log(error) );
// Case 2: without catch
p = Promise.reject('my error 2');

p.catch( error => console.log(error) );

请注意第二次拒绝是如何在第一次拒绝之前报告的。这是唯一的区别。

【讨论】:

【参考方案4】:

所以听起来你的问题是,“在承诺链中,.catch() 方法有什么作用?”

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw

throw 语句“将停止(throw 之后的语句不会被执行),控制权将传递给调用堆栈中的第一个catch 块。如果调用函数之间不存在catch 块,则程序将终止。 "

在承诺链中,.then() 方法将返回某种类型的数据块。该块的返回将完成承诺。数据的成功返回完成了承诺。您可以以相同的方式考虑.catch() 方法。 .catch() 但是将处理不成功的数据检索。 throw 语句完成了 Promise。偶尔,您会看到开发人员使用.catch((err) => console.log(err)),这也将完成承诺链。

【讨论】:

【参考方案5】:

您实际上不需要重新抛出它,只需将 Promise.catch 留空,否则它会认为未处理拒绝,然后将代码包装在 try catch 中,它会自动捕获正在传递的错误.

try
  promise.then(function(result)
    //some code
  ).catch(function(error) 
    //no need for re throwing or any coding. but leave this as this otherwise it will consider as un handled
  );
catch(e)
  console.log(e);
  //error can handle in here

【讨论】:

我认为,catch empty 会默默忽略异常,这与 OP 场景不同,会将异常传播到下一个级别【参考方案6】:

在promise链中,最好使用.catch

函数 f2 中的前:.then(...).catch(e => reject(e));

test1 - 使用 try catch test2 - 没有 try 或 .catch test3 - 使用 .catch

function f1() 
    return new Promise((resolve, reject) => 
        throw new Error('test');
    );


function f2() 
    return new Promise((resolve, reject) => 
        f1().then(value => 
            console.log('f1 ok ???');
        ).catch(e => reject(e));
    );


function test1() 
    console.log('test1 - with try catch - look in F12');
    try 
      f2().then(() =>  // Uncaught (in promise) Error: test
        console.log('???'); );
     catch (e) 
      console.log('this error dont catched');
    


function test2() 
    console.log('test2 - without try or .catch - look in F12');
    f2(); // Uncaught (in promise) Error: test


function test3() 
  console.log('test3 - with .catch');
  f2().then(value => 
    console.log('??');
  ).catch(e => 
    console.log(' now its ok, error ', e);
  )


setTimeout(() =>  test1(); 
  setTimeout(() =>  test2(); 
    setTimeout(() =>  test3(); 
    , 100);
  , 100);
, 100);

【讨论】:

以上是关于重新抛出 promise catch 中的错误的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能在 Promise.catch 处理程序中抛出?

节点使用带有 .catch() 语句的 Promise.all 为 Mongoose 请求抛出 UnhandledPromiseRejectionWarning

拦截全局的 Promise unhandledRejection 错误,并阻止控制台输出

es6 promise的catch 和 then 的区别认识

Promise中的then第二个参数和catch有什么区别?

为啥即使我 .catch() Promise.all() 也会抛出异常?