通常异常使用方式:
try { JSON.parse(json); } catch (e) { // TODO }
但是这对于异步编程而言并不一定适应,下面的代码 catch 不会捕捉到异常,并导致程序退出:
async function action(){ setTimeout(()=>{ throw new Error("发生了错误"); }, 1000); } try { action(); } catch (e){ console.log('catch: ', error.message); }
异步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/