Nodejs -- 异步编程
本文主要介绍一些偏基础和概念性的东西,尽量用简单的语言来阐明这些点,作为为Node做前提准备,同时也是知识整理。
1. 通常的语言是是不接受把方法直接作为参数的,当然在一些语言中也是会有变通的,入C#中有委托,代理,不过在JavaScript中是可以直接把方法(function)作为参数传入另一方法的这种方式,也就是咱们通常所说的高阶函数。如下:
var functionAsParam = function(){
console.info("I am a param");
}
(function(functionAsParam){
functionAsParam();
})()
2. JS中偏函数的用法,也就是利用apply,call等函数,如下:
var toString = Object.prototype.toString;
function type(obj){
return function(param){
var result = toString.call(obj);
console.info("[object " + typeof(param) + "]" == result);
}
}
var isString = type(String());
isString("qiang.c.chen");
注意上面的例子有个额外的支持点闭包,函数执行的作用域不同而造成内部可以访问到外部的这种现象。换句话来说上面的functionType方法本来已经执行完毕,本应该会被销毁,但是因为它返回的是个方法(这也是JS的一个特点,可以把方法作为返回值,通常语言中是不行的),而这个方法又是单独的作用域,或者说有单独的存储空间,所以不会被销毁。
3. Node中事件的发布与订阅
callback回掉又称发布/订阅模式,Node自身提供的events模块是该模式的简单事件,它具有addListener/on(),once(),removeListerner(),removeAllListeners(),emit()等基本的事件监听模式的方法实现。操作简单如下:
订阅 发布
emitter.on("event1", function(message){ }) emitter.emit("event1", "I am a trigger")
可以为一个事件添加多个监听,同时监听器也可以灵活的添加和删除。
· Node中继承events模块如下:
var util = requuire('util');
var events = require("events");
function TestObj(){
events.EventEmitter.call(this);
}
util.inherits(TestObj, events.EventEmitter);
4. Promise/Deferred模式,为了解决深度嵌套问题也就是所谓的“恶魔金字塔”,保持代码的整洁。在此基础上比较经典的封装解决掉了多异步协作,序列化执行的Promise,Promise化API等问题。
Promise 可以理解为是事件的监听+缓存。
Deffered 可以理解为延迟对象,也就是事件的触发与发布,这里是重点需要封装异步的调用,回调函数的注入。
// Promise
var Promise = function(){
EventEmitter.call(this);
}
util.inherits(Promise, EventEmitter);
Promise.Prototype.then = function(successHandler, errorHandler, progressHandler){
this.once('success', successHandler);
this.once('error', errorHandler);
this.once('progress', progressHandler);
return this;
}
// Deferred
var Deferred = function(){
this.state = '';
this.promise = new Promise();
}
Deferred.prototype.resolve = function(obj){
this.state = 'fulfilled';
this.promise.emit('success', obj);
}
Deferred.prototype.reject = function(err){
this.state = 'failed';
this.promise.emit('error', err);
}
Deferred.prototype.progress = function(data){
this.promise.emit('progress', err);
}
// API
var promisify = function(res){
var deferred = new Deferred();
var result = '';
res.on('data', function(callbackResult){
result += callbackResult;
deferred.progress(chunk);
});
res.on('end', function(){
promise.resolve(result);
});
res.on('error', function(err){
promise.reject(err);
});
return deferred.promise;
}
// Invoke API
promisify(res).then(function(callbackResult){
// do some logic
},
function(error){
console.info(error);
},
function(){
})
以上为Promise/Deferred的基本原理,如在实际应用中直接用顺手封装完的库即可,例如when,Q等。
5. 流程控制,最知名的流程控制模块async,值得注意的是callback,这里的callback是由async通过函数注入进来的,而不是使用者指定的。
· 任务串行
async.series([
function(callback){
fs.readFile('file1.txt', 'utf-8', callback);
},
function(callback){
fs.readFile('file2.txt', 'uft-8', callback);
}],
function(err, results){
// todo error
}
)
· 任务并行
sync.parallel([
function(callback){
fs.readFile('file1.txt', 'utf-8', callback);
},
function(callback){
fs.readFile('file2.txt', 'utf-8', callback);
}],
function(err, results){
// todo error
}
)
· 异步调用的依赖处理
async.waterfall([
function(callback){
fs.readFile('file1.txt', 'utf-8', function(err, content){
callback(err, content);
});
},
function(arg1, callback){
fs.readFile(arg1, 'utf-8', function(err, content){
callback(err, content);
});
}],
function(err, result){
// todo err
}
)
同时对于复杂的业务逻辑可以使用auto(obj)方法,obj为一个可配置的对象。其他流程控制库Step,EventProxy,wind等,还有通过源代码编译的方式来实现流程控制。