Node.js 异步异常处理

通常异常使用方式:

try { 
 JSON.parse(json); 
} catch (e) { 
 // TODO 
}


但是这对于异步编程而言并不一定适应。异步I/O的实现主要包含两个阶段:提交请求和处理结果。这两个阶段中间有事件循环的调度,两者彼此不关联。异步方法则通常在第一个阶段提交请求后立即返回,因为异常并不一定发生在这个阶段,try/catch的功效在此处不会发挥任何作用。异步方法的定义如下:

try{
  process.nextTick(callback);
}catch(e){
  //todo...
}


调用async()方法后,callback被存放起来,直到下一个事件循环(Tick)才会取出来执行。尝试对异步方法进行try/catch操作只能捕获当次事件循环内的异常,对callback执行时抛出的异常无能为力。

try{
  async(callback);
}catch(e){
  //todo...
}


Node在处理异常上形成了一种约定,将异常作为回调函数的第一个实参传回,如果是空值,则表明异步调用没有异常抛出:

async(function(err, results){
  //todo...
});


在便携异步方法上,需要去遵循这样一些原则:

原则一,必须执行调用者传入的回调函数;

原则二,正确传递回异常供调用者判断。


另一个容易犯的错误是对用户传递的回调函数进行异常捕获,示例代码如下:

try{
  req.body=JSON.parse(buf,options.revier);
  callback();
}catch(err){
  err.body=buf;
  err.status=400;
  callback(err);
}


上述代码中,如果回调函数中出现异常,那么进入catch代码中执行,于是回调函数被执行了2次。正常的应该是:

try{
  req.body=JSON.parse(buf,options.revier);
}catch(err){
  err.status=400;
  return callback(err);
}
callback();


在编写异步方法时,只要将异常正确地传递给用户的回调函数即可,无需过多处理。



try/catch 之痛

一般情况下,我们会将有可能出错的代码放到 try/catch 块里。但是到了 Node.js,由于 try/catch 无法捕捉异步回调里的异常,Node.js 原生提供 uncaughtException 事件挂到 process 对象上,用于捕获所有未处理的异常:

process.on('uncaughtException', function(err) {
  console.error('Error caught in uncaughtException event:', err);
});
 
try {
  process.nextTick(function() {
    fs.readFile('non_existent.js', function(err, str) {
      if(err) throw err;
      else console.log(str);
    });
  });
} catch(e) {
  console.error('Error caught by catch block:', e);
}


执行的结果是代码进到了 uncaughtException 的回调里而不是 catch 块。 uncaughtException 虽然能够捕获异常,但是此时错误的上下文已经丢失,即使看到错误也不知道哪儿报的错,定位问题非常的不利。而且一旦 uncaughtException 事件触发,整个 node 进程将 crash 掉,如果不做一些善后处理的话会导致整个服务挂掉,这对于线上的服务来说将是非常不好的。


使用 domain 模块捕捉异常

随 Node.js v0.8 版本发布了一个 domain(域)模块,专门用于处理异步回调的异常,使用 domain 我们将很轻松的捕获异步异常:


运行上面的代码,我们会看到错误被 domain 捕获到,并且 uncaughtException 回调并不会执行,事情似乎变得稍微容易些了。



参考:

《Node.js 深入浅出》

https://blog.csdn.net/zgrbsbf/article/details/79485494

http://www.alloyteam.com/2013/12/node-js-series-exception-caught/


真诚赞赏,手留余香
赞赏
随机推荐
体块透视
在 CentOS 8 上使用 DNF 管理软件包
Javascript 获取时间戳
node.js 的 module.exports 和 exports 的区别
微信上传图片
框架执行原生的SQL语句
mint-ui中的cell无法绑定原生事件
NodeJs通过async/await处理异步
Thinkphp5 rc4 升级到Thinkphp5正式版遇到的一个问题
Chrome开发者工具的实用技巧(译)