警示后人系列:为什么我没有catch到回调函数中抛出的错误?

本文讲述了在koa框架中如何正确处理异步回调中的错误,避免进程崩溃,并提供了使用Promise及async/await进行错误处理的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天犯了一个js中的错误,记录一下警示后人:)

事情是这样,koa会帮我们捕获中间件中抛出的错误,从而不让服务器崩溃,如下图:
clipboard.png
服务器为此次请求返回500,仍然能够处理后续的请求。

从这一点出发,我就认为,我可以在中间件里面随便throw,这样koa就能在控制台帮我打印出所有错误的信息,反正也不会对后续请求造成影响。

但是今天的错误是这样的:
clipboard.png
进程居然崩溃了!这样上线岂不是要出事?

为什么koa这回没有捕获到我throw的错误呢?

用简单的代码模拟一下这个场景:

try {
  setTimeout(() => {
    try {
      throw 500;
    } catch (err) {
      console.log('err1', err);  // called
      throw err;
    }
  }, 0);
} catch (err) {
  console.log('err2', err)  // never called
}

clipboard.png
可以看出,内层的try确实捕获到了错误,但是当我们把这个错误继续抛出,外层的try却没有捕获到这个错误

原因在于,回调函数被异步调用时,外层try中的代码其实已经执行完了,栈帧已经从执行栈中弹出。回调函数的栈帧被放入时,执行栈是空的

错误被抛出后,沿着执行栈,希望找到“外层”函数的try...catch语句。可是这个回调函数根本就没有“外层”函数了,因此这是一个没有被捕获到的错误,这会造成进程的崩溃。

总结

用try语句包裹回调函数的定义,无法捕获到回调函数中的错误。必须用try语句包裹运行时的外层函数。如果回调函数运行时没有外层函数,你必须在回调函数内部做错误的捕获和处理。

如果回调函数定义在Promise中,你可以直接在回调函数中调用reject(reason),让这个Promise的订阅者来处理错误:

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    try {
      throw 500;
    } catch (err) {
      console.log('err1', err);  // called
      reject(err);
    }
  }, 0);
});

p.catch(err => {
  console.log('err3', err);  // called
});

如果回调函数由async function的await关键字来执行,那么可以通过reject Promise,让async function中的try...catch捕获到错误。

async function fun() {
  try {
    await new Promise((resolve, reject) => {
      setTimeout(() => {
        try {
          throw 500;
        } catch (err) {
          console.log('err1', err);  // called
          reject(err);
        }
      }, 0);
    });
  } catch (err) {
    console.log('err4', err);  // called
    throw err;
  }
}
try {
  fun();
} catch (err) {
  console.log('err5', err);  // not called
}

注意try {fun();}无法捕获到错误,因为这个函数不是通过await来执行的。错误抛出的时候,这个try语句早已经执行完。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值