最近接触了nodejs服务器端编程,于是准备拿它与mongodb做个新项目。
因为有js的基础,上手起来很快,另外mongodb操作的也是json数据,所以功能做起来还算一帆风顺,但有个问题一直让我很恼火,就是数据处理的回调函数层层嵌套,而后面又落下长长的方法关闭尾巴,心里很是不爽。
由于回调函数是为了实现非阻塞编程,也是nodejs的最大亮点,没有回调则不可能有nodejs作为服务器端应用程序的角色。于是我开始琢磨能不能让层层嵌套的回调函数平行起来,再用js的函数变量串行化起来呢?
在一段时间的钻研与试验后,有了实现这个想法的工具类:
/*
[@author](/user/author) chenliang
[@param](/user/param) jobCtx a json object as context param
[@param](/user/param) callbackArray an array object of callback function
*/
var SeqJobCtx = module.exports = function(jobCtx,callbackArray) {
this.ctx=jobCtx;
this.callbacks=callbackArray;
this.next=function(){
var cbm=this.callbacks.shift();
if(cbm!=null){
cbm(this);
}
};
this.start=function(){
this.next();
};
};
我且把它叫做SeqJobCtx类,是不是很小巧?下面来看看它有何本事:
var mongodb = require("mongodb");
//引入SeqJobCtx类
var SeqJobCtx=require("./SeqJobCtx");
console.log("start on :"+__dirname);
//连接mongodb
var serv = new mongodb.Server('localhost', 27017, {});
var dbManager = new mongodb.Db('db', serv);
//正式操作mongodb
dbManager.open(function (error, db) {
if(error) throw error;
var ctx=new SeqJobCtx({"db":db},[drop_capCol,create_capCol,query_capCol,closeDB]);
ctx.start();
});
function create_capCol(seqCtx){
var db=seqCtx.ctx.db;
db.createCollection("capCol",{capped:true,size:1000000,max:100},function(err,coll){
if(err){
console.log("Create capCol failed:"+err);
}else{
console.log("Collection capCol created.");
}
coll.insert({"hello":"world"},function(err,rl){
if(err){
console.log("Insert failed:"+err);
}else{
console.log("Insert finished.");
}
seqCtx.next();
});
});
}
function drop_capCol(seqCtx){
var db=seqCtx.ctx.db;
db.dropCollection("capCol",function(err){
if(err){
console.log("Drop capCol failed:"+err);
}else{
console.log("Drop capCol ok.");
}
seqCtx.next();
});
}
function query_capCol(seqCtx){
var db=seqCtx.ctx.db;
db.collection("capCol",function(err,coll){
coll.find().limit(5).toArray(function(err,array){
console.log("array.length:"+array.length);
for(var i=0;i<array.length;i++){
console.log("result:"+JSON.stringify(array[i]));
}
seqCtx.next();
});
});
}
function closeDB(seqCtx){
var db=seqCtx.ctx.db;
db.close();
console.log("DB closed.");
seqCtx.next();
}
相信大家还是能够很容易读懂的了,构造SeqJobCtx的第一个参数是个json对象,我传入db作为上下文参数,这个上下文参数在整个作业运行期间都是有效的;第二个参数是方法名参数数组,看到了吧对应着后面四个function,分别是删除collection,建立collection,查询collection,关闭数据库连接。
各个数据库操作步骤都有了自己的名字,长长的方法关闭尾巴不见了,一眼就能读懂整个作业运作,是不是觉着有点象配置工作流的感觉?这样一来,如果以后业务变更了,要把流程改为先建collection,查询,然后删除collection,关闭数据库,你只需要调换一下方法变量数组的顺序就是了,而且还强迫那些习惯了在阻塞式程序中,一个方法写个几百上千行代码的开发人员优化结构,提高代码可读性。
最后调用SeqJobCtx的start方法,让它开始为你工作吧!
差点忘了交待两个关键步骤,就是每个被调方法变量的方法都接收一个参数,这个参数其实是SeqJobCtx本身,这个参数为你提供了上下文,可以看到数据库连接就是从这个上下文中得到的: var db=seqCtx.ctx.db; 另外,不要忘了在回调结束的时候调用SeqJobCtx的next方法,这样整个作业才能够串行化起来。
希望这个小小的工具类能够为你的nodejs工作变得舒心顺畅,也相信你很快能够掌握它的工作原理,做出自己功能强大的工具来。
呵呵,之前确实不知道这些库,大致看了下,jscex好像是要编译的?eventproxy是异步调用的,而我的是要解决同步的。 step和我的比较相似,但为什么业务方法要放在step()里面了,没看大明白,这样的实现方法重用性如何,调度起来方便性如何。
这个思路不错,如果可以实现并行执行就更好了。 大概意思是,假设一个情景:A、B、C函数,A、B互不依赖,但C依赖A、B。那么执行A后等待回调的时候就可以执行B,A,B就类似并行执行,再串行执行C。
可以参考一下jquery.deferred的API:http://api.jquery.com/category/deferred-object/ 这里有类似的实现。