在使用 Node.js 开发时,我们经常会写像这样的代码:
var readFile = require('fs').readFile;
...
readFile('a.js', {encoding:'utf8'}, function (err, dataA) {
if (err) {
return callback(err);
}
console.log(dataA);
readFile('b.js', {encoding:'utf8'}, function (err, dataB) {
if (err) {
return callback(err);
}
console.log(dataB);
readFile('c.js', {encoding:'utf8'}, function (err, dataC) {
if (err) {
return callback(err);
}
console.log(dataC);
...
});
});
});
...
每一个函数执行完毕都会检查是否有错误,有错误则 callback 返回,没有错误则继续往下执行。使用 async 模块后:
var async = require('async');
var readFile = require('fs').readFile;
...
async.series([
function (done) {
readFile('a.js', {encoding:'utf8'}, function (err, dataA) {
console.log(dataA);
done(err);
});
},
function (done) {
readFile('b.js', {encoding:'utf8'}, function (err, dataB) {
console.log(dataB);
done(err);
});
},
function (done) {
readFile('c.js', {encoding:'utf8'}, function (err, dataC) {
console.log(dataC);
done(err);
});
}
], callback);
...
使用 Promise(以 fs-promise
模块为例):
var readFile = require('fs-promise').readFile;
readFile('a.js', {encoding:'utf8'})
.then(function (dataA) {
console.log(dataA);
return readFile('b.js', {encoding:'utf8'})
})
.then(function (dataB) {
console.log(dataB);
return readFile('c.js', {encoding:'utf8'})
})
.then(function (dataC) {
console.log(dataC);
})
.catch(function (err) {
console.log(err);
});
使用 co 模块(以 co-fs
模块为例):
var readFile = require('co-fs').readFile;
var co = require('co');
co(function* () {
var dataA = yield readFile('a.js', {encoding:'utf8'});
console.log(dataA);
var dataB = yield readFile('b.js', {encoding:'utf8'});
console.log(dataB);
var dataC = yield readFile('c.js', {encoding:'utf8'});
console.log(dataC);
}).catch(function (err) {
console.log(err);
});
可以看出,使用生成器函数+yield 不仅解决了回调嵌套的问题,还使代码变得简洁易读、错误处理变得更为优雅,任意一个文件读取失败抛出的异常都会被 co 的 catch 捕获。
koa 是基于 co 开发的,所以可以在 koa 中使用 co 支持的所有特性。因为每一个中间件都是一个生成器函数,中间件又是通过 next 层层传递的,所以我们通常将错误处理放到第一个中间件,如下代码所示:
var app = koa();
app.use(function* (next) {
try {
yield next;//此处next代表下游所有中间件
} catch(err) {
console.log(err);
}
});
app.use(logger());
app.use(bodyparser());
...
这种方式能够捕获大多数的错误,但捕获不了没有使用 yield 的异步函数的错误和事件错误等。koa-errorhandler 是 koa 的一个错误处理中间件,使用如下:
var koa = require('koa');
var errorHandler = require('koa-errorhandler ‘);
var app = koa();
app.use(errorHandler());
app.use(function* () {
foo();// 将会被 errorHandler 捕获 foo is not defined
});
app.listen(3000, function () {
console.log('listening on port 3000.');
});
koa-errorhandler 能够根据请求头中 Accept 的不同返回不同类型的错误响应(html/json/text),用户也可自定义错误处理器,详见 koa-errorhandler 的 readme。