我们都知道异步IO是node.js的一大特色和优势,看了朴灵大大的深入浅出node.js的异步IO一章,还是觉得理解不够深入,但是学习是有过程的,万丈高楼平地起,多总结多实践就能提高。根据自己的学习经历,总结一下异步控制。
方法1:
通过异步嵌套来实现,例如:通过读取A.txt文件中的内容---B.txt,将其作为目标进一步去读取B.txt
var fs=require('fs');
fs.readFile('A.txt','utf8',function(err,data){ //读取出来是2进制形式的数据,所以要指定编码
fs.readFile(data,'utf8',function(err,data){
console.log(data)
})
})
方法2:
通过事件监听的方式来进行异步控制。Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。Node.js里面的许多对象都会分发事件:一个net.Server对象会在每次有新连接时分发一个事件, 一个fs.readStream对象会在文件被打开的时候发出一个事件。 所有这些产生事件的对象都是 events.EventEmitter 的实例。
var event=require('events');
var eventMitter=new event;
var fs=require('fs');
var eventMitter=new event;
eventMitter.on('start',function(data){
fs.readFile(data,'utf8',function(err,data){
console.log(data);
})
});
fs.readFile('A.txt','utf8',function(err,data){
eventMitter.emit('start',data);
});
方法3:
Promise。它表示一个异步操作的最终结果 。相当于:服务员下单后去干别的,做好后会端饭,promise是承诺,做好后可能还会做一些其他处理。
//这是一个需要延迟处理的任务
var Defer = function(){
var status = 'pending';//初始态
var value;//异步操作的最终的值
var callback;//异步操作成功后的回调
return {
//当调用resolve的时候就表示成功了
//异步操作完成之后调用,表示操作成功了
resolve:function(_value){
value = _value;
status = 'resolve';
callback(value);
},
//就是承诺的对象,它会返回给客户端
promise:{
then:function(_callback_){
callback = _callback_;
}
}
}
}
var defer = Defer();
var fs = require('fs');
fs.readFile('1.txt','utf8',function(err,data){
defer.resolve(data);//读取文件完成后调用resolve把状态改为成功
})
var promise = defer.promise;
//defer给你一个承诺,当异步操作完成之后我会调用你传给我回调函数
promise.then(function(data){
console.log(data);
});
方法4:
使用async模块,这是一个非常强大的模块。下面是它的几种常用方法的实例:
①series方法:串行执行异步任务,cb函数相当于express框架中的配置对象app中的next函数。当cb的第一error参数有值时,则不往下执行下一个函数,直接到series的回调函数处。
<pre name="code" class="javascript">var async=require('async');
async.series([
function(cb){
setTimeout(function(){
console.log('看电视');
cb(null,'看电视');
},3000);
},
function(cb){
setTimeout(function(){
console.log('写作业');
cb(null,'做作业');
},3000);
}
],function(err,result){
console.log(err);
console.log(result);
})
执行结果:
②parallel方法:并行执行。比如同时获取数据和渲染模板,就是此方法的应用场景。
var async=require('async');
async.parallel([
function(cb){
setTimeout(function(){
console.log('看电视');
cb(null,'看电视');
},3000);
},
function(cb){
setTimeout(function(){
console.log('写作业');
cb(null,'做作业');
},3000);
}
],function(err,result){//当上面两个异步任务 执行完后执行的回调
console.log(err);
console.log(result);
})
执行结果:
③waterfall方法:
//waterfall:是串行的,但是每个函数产生的返回值会传递给第二个函数
var async=require('async');
async.waterfall([
function(cb){
setTimeout(function(){
//console.log(data);
cb(null,'看电视');
},1000);
},
function(data,cb){
setTimeout(function(){
//console.log(data)
cb(null,data+'做作业');
},1000);
},
function(data,cb){
setTimeout(function(){
cb(null,data+'睡觉');
},1000);
}
],function(err,result){//当上面两个异步任务 执行完后执行的回调
console.log(err);
console.log(result);
})
执行结果:
④foreach方法:
var async=require('async');
var arr=[1,2,3];
//串行迭代,并在迭代完成后执行回调
console.time('cost')
/*
async.eachSeries(arr,function(item,cb){
setTimeout(function(){
console.log(item);
cb();
},1000)
},function(){
console.timeEnd('cost')
});
*/
//同时执行,并行迭代
async.forEach(arr,function(item,cb){
setTimeout(function(){
console.log(item);
cb();
},1000)
},function(){
console.timeEnd('cost')
});
⑤auto方法:并行和串行执行都有时,举例
var async=require('async');
async.auto({
mix:['water','flour',function(results,cb){
//console.log(arguments)
//console.log(results);
cb(null,results.water+'+'+results.flour);
}],
steam:['mix',function(results,cb){
cb(null,results.mix+'+'+'蒸')
}],
water:function(sb){
console.time('cost')
sb(null,'水')
//console.log('1')
},
flour:function(cb){
cb(null,'面粉')
},
//定义依赖关系,results是个对象
}, function(err,result){//result是个对象
console.timeEnd('cost')
console.log(result);
});
执行结果: