当前位置: 首页 > 后端技术 > Node.js

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

时间:2023-04-03 20:53:06 Node.js

今天js出错了,记录下来以警示后人:)事情是这样的,koa会帮我们捕获中间件抛出的错误,以免服务器崩溃,如下图:本次请求的服务器如果返回500,后续请求仍然可以处理。从这一点出发,我觉得我可以在中间件里随便扔,让koa帮我在控制台打印出所有的错误信息,反正不会影响后面的请求。但是今天的错误是这样的:进程居然崩溃了!这样上网不就出事了吗?为什么koa没有捕捉到我这次抛出的错误呢?用简单的代码模拟这个场景:}catch(err){console.log('err2',err)//nevercalled}可以看出,内层try确实捕获到了错误,但是当我们继续抛出这个错误时,外层try却没有抓住这个错误。原因是异步调用回调函数的时候,实际上已经执行了外层try中的代码,栈帧已经从执行栈中弹出。当定时器时间到达时,执行引擎将回调函数压入空栈,即回调函数在执行栈的底部。如果回调函数没有捕获到错误,错误将被全局泄漏。在节点中,全局接收错误将使进程崩溃。总而言之,当抛出错误时,它会在执行堆栈中向上查找处理程序。运行时的执行栈结构往往与程序员看到的“代码块结构”不同:回调函数的定义被try语句包裹起来,无法捕获回调函数中的错误。如果回调函数运行时没有外层函数,则必须在回调函数内部进行错误捕获和处理。如果回调函数定义在一个Promise中,你可以直接在回调函数中调用reject(reason)让这个Promise的订阅者处理错误:constp=newPromise((resolve,reject)=>{setTimeout(()=>{try{throw500;}catch(err){console.log('err1',err);//调用了reject(err);}},0);});p.catch(err=>{console.log('err3',err);//调用});如果回调函数是通过async函数的await关键字执行的,那么async函数中的try...catch可以通过rejectingPromise来捕获错误。异步函数fun(){try{awaitnewPromise((resolve,reject)=>{setTimeout(()=>{try{throw500;}catch(err){console.log('err1',err);//调用reject(err);}},0);});}catch(err){console.log('err4',err);//调用抛出错误;}}try{fun();}catch(err){console.log('err5',err);//notcalled}注意try{fun();}不能捕获错误,因为这个函数不是通过await执行的。抛出错误时,try语句已经执行。