-
Notifications
You must be signed in to change notification settings - Fork 396
08 如何避免回调地狱
孙正华 edited this page Jul 26, 2018
·
3 revisions
Node.js 是单线程的,任何耗时的操作都应该使用异步去执行。
JavaScript 中的异步实现常见的有回调函数和 Promise,这里有一篇很好的学习 Promise 的中文教程。
回调函数应该是异步实现最基本的方式,但很容易写出类似的代码:
fn1(function(data1){
fn2(data1, function(data2){
fn3(data2, function(){
//...
});
});
});
当嵌套层级越来越多,代码将趋于>
形,难以阅读和维护,于是有人称之为回调地狱。此时可以使用 async 来解决这个问题(如果你的浏览器或 Node.js 版本较高,你也可以直接使用原生 Promise)。
安装 async:
$ npm install --save async
async 最常用的 2 个方法介绍:
- parallel 异步执行各个方法,方法间不能有依赖。
async.parallel([
//异步方法1
function(callback){
setTimeout(function(){
//执行结果为 'one'
callback(null, 'one');
}, 200);
},
//异步方法2
function(callback){
setTimeout(function(){
//执行结果为 'two'
callback(null, 'two');
}, 100);
}
],
//可选,全部异步方法执行完成后回调
function(err, results){
//results 是执行结果的数组,按照上面异步方法1、异步方法2的定义的顺序,此处 results = ['one','two']
});
实例:
https://github.com/eshengsky/iBlog2/blob/master/routes/blog.js#L16-L46
async.parallel([
// 获取配置
function (cb) {
tool.getConfig(path.join(__dirname, '../config/settings.json'), (err, settings) => {
if (err) {
cb(err);
} else {
cb(null, settings);
}
});
},
// 获取分类
function (cb) {
category.getAll((err, categories) => {
if (err) {
cb(err);
} else {
cb(null, categories);
}
});
}
], (err, results) => {
let settings,
categories,
cate;
if (err) {
next(err);
} else {
settings = results[0];
categories = results[1];
}
});
- waterfall 依次执行任务数组中的方法,并将前一个方法的返回值作为参数传入下一个方法。
async.waterfall([
//方法1
function(callback) {
//返回结果 'one','two',作为下一个方法的参数
callback(null, 'one', 'two');
},
//方法2,其中 arg1 = 'one',arg2 = 'two'
function(arg1, arg2, callback) {
//返回结果 'three',作为下一个方法的参数
callback(null, 'three');
},
//方法3,其中 arg1 = 'three'
function(arg1, callback) {
//返回结果 'done',作为最终回调函数的参数
callback(null, 'done');
}
], function (err, result) {
//result = 'done'
});
实例:
https://github.com/eshengsky/iBlog2/blob/master/routes/blog.js#L68-L124
async.waterfall([
// 1. 根据分类alias获取分类对象
function (cb) {
category.getByAlias(req.body.CateAlias, (err, category) => {
if (err) {
cb(err);
} else {
cb(null, category);
}
});
},
// 2. 传入分类对象查询文章
function (category, cb) {
const params = {
cateId: category._id,
pageIndex: req.body.PageIndex,
pageSize: req.body.PageSize,
sortBy: req.body.SortBy,
keyword: req.body.Keyword,
filterType: req.body.FilterType
};
//...
}
], (err, result) => {
if (err) {
cb(err);
} else {
cb(null, result);
}
});