Node 异常捕获
程序媛徐婵 人气:0常见Node报错处理机制
try catch
try...catch是大家最常用的错误处理机制,Javascript语言内置的错误处理机制可以在检测到代码异常的时候直接进行捕获并处理。
function test() { try { throw new Error("error"); } catch(e) { console.log(e); } finally { console.log("finally"); } } test()
一般来说,throw 用于抛出异常,但是单纯从语言的角度,我们可以抛出任何值,也不一定是异常逻辑,但是为了保证语义清晰,不建议用 throw 表达任何非异常逻辑。try 语句用于捕获异常,用 throw 抛出的异常,可以在 try 语句的结构中被处理掉:try 部分用于标识捕获异常的代码段,catch 部分则用于捕获异常后做一些处理,而 finally 则是用于执行后做一些必须执行的清理工作。catch 结构会创建一个局部的作用域,并且把一个变量写入其中,需要注意,在这个作用域,不能再声明变量 e 了,否则会出错。在 catch 中重新抛出错误的情况非常常见,在设计比较底层的函数时,常常会这样做,保证抛出的错误能被理解。finally 语句一般用于释放资源,它一定会被执行,我们在前面的课程中已经讨论过一些 finally 的特征,即使在 try 中出现了 return,finally 中的语句也一定要被执行。
Node原生错误处理机制
大多数Node.js核心API都提供的是利用回调函数处理错误,之所以采用这种错误处理机制,是因为异步方法所产生的方法并不能简单地通过try...catch机制进行拦截。
const fs = require('fs'); function read() { fs.readFile("/some/file/does-not-exist", (err, data) => { if(err) { throw new Error("file not exist"); } console.log(data); }); } read();
Promise
Promise是用于处理异步调用的规范,由于 JavaScript 特殊的 EventLoop 机制,由 Promise 异步产生错误是没有办法使用 try...catch
的。
Promise提供的错误处理机制,是通过catch方法进行捕获。
try { Promise.reject() } catch(err) { // 这里啥都 catch 不到 console.log(err) }
fs.copy( buildStatic, aresStatic ).then(() => { console.log(`${buildStatic} -> ${aresStatic}`) }).catch(err => { // 这里可以捕获到报错 console.log(err) })
async/await + try catch
async/await语法糖加上try...catch语句进行的。这样做的好处是异步和同步调用都能够使用统一的方式进行处理了。
对于异步代码,建议统一转换成Promise然后采用async/await + try...catch这种方式进行处理。这样风格统一,程序的健壮性也大大加强。
async function one() { // a未定义 a.b = 3 } async function test() { try { await one(); } catch(error) { // a is not defined console.log(error); } } test();
unhandledRejection
实际开发中,总是会有一些 Promise 被遗漏掉catch处理,没有得到错误处理,会导致应用crash。我们可以通过**unhandledrejection
** 事件捕获未处理的 Promise 错误。
实现原理:Node.js 会在每次 Tick 执行完后检查是否有未捕获的错误 Promise,如果有,则触发 unhandledRejection
事件。
process.on('unhandledRejection', (reason, p) => { console.log('Unhandled Rejection at:', p, 'reason:', reason); });
特殊情况如何捕获异常
如果是回调函数中捕获异常怎么做?用domain去捕获,domian捕获会抛出500错误,但是domain捕获有一个问题,会丢失栈信息,无法保证程序健康进行,所以要结束进程,在回调函数中process.exit(1)
,然后用node的server.close方法再去释放,server.close连接释放后自动结束进程,所以不用在server.close中去结束进程process.exit(1)
uncaughtExpection捕获异常的的原理就是:uncaughtExpection事件存在回调函数process.on("uncaughtExpection",callback)
时node不会强制结束进程,这样可弥补domain丢失stack的问题
所以domian去捕获绝大部分回调函数中的异常,uncaughtExpection去捕获丢失stack异常,这样就完整了
app.use(function(req,res,next){ var reqDomain = domain.create(); reqDomain.on("err",function(){ try { var killTimer = setTimeout(function(){ process.exit(1); },1000) killTimer.unref(); server.close(); res.send(500); } catch(e) { // statements console.log(e.stack); } }) reqDomain.run(next); }); process.on("uncaughtException",function(err){ console.log(err); try{ var killTimer = setTimeout(function(){ process.exit(1) },1000) killTimer.unref(); server.close(); }catch(e){ console.log(e.stack); } });
uncaughtException
uncaughtException
也是 NodeJS 进程的一个事件。如果进程里产生了一个异常而没有被任何Try Catch
捕获会触发这个事件。
NodeJS 对于未捕获异常的默认处理是:
- 触发 uncaughtException
事件
- 如果 uncaughtException 没有被监听
- 打印异常的堆栈信息
- 触发进程的 exit 事件
所以如果某个报错没有被任意try catch捕获,且没有定义uncaughtException事件,就会导致程序退出。
process.on('unhandledRejection', (reason, p) => { console.log('Unhandled Rejection at: Promise', p, 'reason:', reason) })
Express错误处理
Express中,路由或中间件报错处理可以通过特殊的中间件来完成。
一般中间件的参数为3个:req
,res
, next
。如果你use
一个4个参数的中间件,它将被Express视为错误处理中间件。
app.get('/a',function(req,res,next){ res.end('hahah'); next(new Error('错误啦')); }); app.use('/a',function(err,req,res,next){ console.log('路由错误'+err); }) //all error中间件 app.use(function(err, req, res, next) { console.log("Error happens", err.stack); }); //错误传递,/a的错误处理首先匹配/a那个错误中间件,如果不用next就不会传递到全局错误处理中间件 //如果在/a错误处理中间件里调用next(err) 那么全局错误中间件也会被执行
加载全部内容