Nodejs异步流程框架async
标签(空格分隔): Node.js
[TOC]
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。nodejs是基于异步的写法,有时一个函数需要上一个函数的返回值做参数,这样下来一不小心就会陷入深层嵌套回调地狱的陷阱中。
下面的一个相当简单的需求
将A、B、C文件里面的内容读取出来存到D文件中
普通的写法如下
var fs = require('fs');
fs.readFile('D:\\test\\A.txt',function(err,data1){
fs.readFile('D:\\test\\B.txt',function(err,data2){
fs.readFile('D:\\test\\C.txt',function(err,data3){
console.log(data1+'>>>'+data2+'>>>'+data3);
fs.writeFile('D:\\test\\D.txt',data1+'>>>'+data2+'>>>'+data3,function(err){
console.log(err);
});
});
});
});
如果再来个a,b,c等等文件,各种嵌套,一是看起来很凌乱,二是后来维护起来是相当的困难!
那么为了避免这样的问题,也有一些模块来解决,比如Promise,Async,co等等,使用哪种来控制异步流程,我也纠结了许久,最终选择使用Async,有人说Async是一代,promise是二代,co是三代。于是毫不犹豫的选择了Async.
Async框架地址
-
Async
在此推荐相当详细的demo地址 - async框架详细Demo
我们常用的是以下四种
- 串行无关联
- 串行有关联
- 并行无关联
- 只能控制
下载async模块
npm install async
串行无关联
多个函数或方法要依次执行,但是他们之间并没有什么联系,只有先后的顺序,比如我要写一个文件,写完之后像用户发送邮件,这两者之间没有必然的联系,但是发邮件必须在写文件完成之后。
async里有一个方法series可以实现这一流程,代码实现如下:
var async = require('async');
console.time('series');
async.series({
one: function(callback) {
callback(null, 'one');//callback('i am err','one');异常处理
},
two: function(callback) {
callback(null, 'two');
},
}, function(error, result) {
//最后结果
console.log('error: ' + error);
console.log('result: ' + result);
console.timeEnd('series');
});
结果
error: null
result: [object Object]
series: 4.472ms
串行流程,one执行完再执行two,如果有three、four等方法,依次写下去,callback(arg1,arg2),两个参数,arg1是异常,arg2是方法的返回值,如果某个函数中arg1不为空,则程序到此终止,之后的不再执行,可自行测试!
注意:waterfall不能使用对象表示法来传递(json格式)
串行有关联
多个函数或方法执行,每一步执行时都需要上一步执行的结果当参数,所以就会有串行等待。async里有waterfall可以实现此场景:
var async = require('async');
console.time('waterfall');
async.waterfall([
function(callback) {
callback(null, 'one');
},
function(onearg, callback) {
callback(null, onearg + '>>>two');
},
function(twoarg, callback) {
callback(null, twoarg + '>>>three');
},
function(threearg, callback) {
callback(null, threearg + '>>>four');
}
], function(error, result) {
console.log('error: ' + error);
console.log('result: ' + result);
console.timeEnd('waterfall');
});
结果
error: null
result: one>>>two>>>three>>>four
waterfall: 4.516ms
这个比较series换了个方法名之外,每个函数多了个参数第二个函数中onearg则是第一个函数的值,以此类推,callback中没变,如果有异常,立即终止可自行验证;
并行无关联
多个函数执行,之间没有任何的关系,也就是说谁执行都行,为了节约时间就可以使用并行流程来解决,如下:
var async = require('async');
console.time('parallel');
async.parallel({
one: function(callback) {
//处理逻辑
callback(null, 'one');
},
two: function(callback) {
//处理逻辑
callback(null, 'tow');
},
three: function(callback) {
//处理逻辑
callback(null, 'three');
},
four: function(callback) {
//处理逻辑
callback(null, 'four');
}
}, function(error, result) {
console.log('one:', result.one);
console.log('two:', result.two);
console.log('three:', result.three);
console.log('four:', result.four);
console.log('error: ' + error);
console.log('result: ' + JSON.stringify(result));
console.timeEnd('parallel');
});
结果
one: one
two: tow
three: three
four: four
error: null
result: {"one":"one","two":"tow","three":"three","four":"four"}
parallel: 4.237ms
并行无关联和串行无关联唯一的差异就是名称不同,但也都是见名知意,主要看执行的时间!!!
智能控制
以上都是纯串行传并行,但是当一个场景里,需要使用串行也需要使用并行的时候,虽然分别写能解决,但是效率不是很高,维护性也不是很好,auto可以解决这一问题。如下场景,参见github示例
需求如下:
- 从某处取得数据
- 在硬盘上建立一个新的目录
- 将数据写入到目录下某文件
- 发送邮件,将文件以附件形式发送给其它人。
可以知道1与2可以并行执行,3需要等1和2完成,4要等3完成。
使用auto来解决
var async = require('async');
console.time('auto');
async.auto({
getData: function(callback) {
setTimeout(function() {
console.log('1.1: got data');
callback(null, 'mydata');
}, 300);
},
makeFolder: function(callback) {
setTimeout(function() {
console.log('1.1: made folder');
callback(null, 'myfolder');
}, 200);
},
writeFile: ['getData', 'makeFolder', function(callback) {
setTimeout(function() {
console.log('1.1: wrote file');
callback(null, 'myfile');
}, 300);
}],
emailFiles: ['writeFile', function(callback, results) {
console.log('emailed file: ', results.writeFile);
callback(null, results.writeFile);
}]
}, function(err, results) {
console.log('err: ', err);
console.log('results: ', results);
console.timeEnd('auto');
});
结果
1.1: made folder
1.1: got data
1.1: wrote file
emailed file: myfile
err: null
results: { makeFolder: 'myfolder',
getData: 'mydata',
writeFile: 'myfile',
emailFiles: 'myfile' }
auto: 650.972ms
将内层callback传递给外层
有时候我们需要封装一个单独的接口供外界调用,但是这个接口内部需要访问Mongodb或redis等异步回调的数据库。这时候我们可能需要将mongodb或redis这些异步回调回来的response作为callback返回给接口.我们可以尝试如下方式
exports.setUserInfo = function(user_id, callback) {
// 串联相关执行
async.warterfull([
getCompanyIdByUserId,
insertMongodb
], function(error, response) {
callback(error, response);
});
// 根据user_id去redis中取出user_info
function getCompanyIdByUserId(callback) {
redis.hget("user_info", user_id, function(error, response) {
// 注意:这里直接callback(error,response)是无法将内层参数传递到外层的
if (error) {
callback(error);
} else {
callback(null, response);
}
})
}
/**
* 将user_info和其他信息组合存储到mongodb中
* @param {*} arg1 :从上一个方法中传递过来的返回值
*/
function insertMongodb(arg1, callback) {
UserMode.create({}, function(error, response) {
if (error) {
callback(error);
} else {
callback(null, response);
}
});
}
}
原文地址:http://blog.csdn.net/qqhjqs/article/details/51913942
推荐文章:Nodejs异步流程控制Async
网友评论