和其他服务器端语言相比,貌似node.js 对于异常捕捉确实非常困难.
首先你会想到try/catch ,但是在使用过程中我们会发现并没有真正将错误控制在try/catch 语句中.
为什么?
答案是这样的:
node.js 是异步IO执行,所以我们将try/catch 反正异步回调函数中,当出现一个异常的时候,我们try 拿到这个错误时应该错过了当前程序运行堆栈,
而在node.js原生的uncaughtException 处理事件却挂在 process 对象上,OMG,你可想而知,如果一个异常出现时,当前运行的 process 会直接挂掉,
导致错误永远不会走到 catch 语句.所以 try/catch 在异步回调中显得苍白无力,我们有什么办法控制这种异步回调的异常吗?
当然,我们下面来介绍一种比较有效的处理方法:
domain模块,这是node自带的一个模块,看名字确实很难联想到他是用来捕捉异常的,它能帮我们做一些try /catch无法做到的事情.
首先引用模块
var domain = require('domain');
如果你也使用Express框架,可以直接将domain 做成一个 Express 的中间件,这样所有的 request及response 中的异步错误都可以捕捉到.
app.configure(function () {
app.use(function (req, res, next) {
var reqDomain = domain.create();
reqDomain.on('error', function (err) { // 下面抛出的异常在这里被捕获,触发此事件
console.log('捕获到错误');
res.send(500, err.stack); // 成功给用户返回了 500
});
reqDomain.run(next);
});
});
上面这种写法是Express 的一个中间件写法, app.use(function(req,res,next){ .....}) 这是一个中间件,用来接收所有http请求,这里你可以捕获request 和 response 对象用来做一些过滤,逻辑判断等等,最后通过 next 来放行本次请求,那么这个中间件就完成了他的一次使命.
而我们为了让 domain 模块来接管所有的http请求中的异常,所以把它写成一个中间件是非常方便的.
然后我们在 process 上将未处理的异常捕捉一下,做到万无一失.
process.on('uncaughtException', function (err) {
console.error("uncaughtException ERROR");
if (typeof err === 'object') {
if (err.message) {
console.error('ERROR: ' + err.message)
}
if (err.stack) {
console.error(err.stack);
}
} else {
console.error('argument is not an object');
}
});
//下面我们来定义几个路由,测试一下.
app.get('/', function (req, res) {
levelDB.getData('test', function (err, v) {
//这里我从levelDB 中读取一条数据,需要引入 levelDB 模块.
err ? res.send({status: "fail"}) : res.send({status: "success", value: v});
});
})
//我们再想办法让程序出现一个异步异常
app.get('/err', function (req, res) {
//throw new Error('exception');
setTimeout(function () {
throw new Error('exception'); // 抛出一个异步异常
}, 1000);
})
//我们之所以想到用 setTimeout 就是想模拟一个异步的回调,如果你直接 throw new Error('exception');这样就不是异步了,直接会被 process 上的 uncaughtException 来接管.
通过上面定义的两个路由,我们来分别访问一下
这个路由我们从数据库读取了一条记录最后返回给客户端,中间没有出现任何异常.
这是我们模拟的一个异常已经被捕捉到,这是服务器端返回给客户端的一个500提示信息,这时我们再看一眼控制台,node 进程并没有挂掉.
每次 domian 捕获到错误后,我都在控制台输出了一行提示信息 "捕获到错误" 当前进程并没有因为异常而挂掉,这就是我们要的效果.