async和eventproxy在流程控制上哪个更优秀
由于node.js特性异步IO,导致 node.js 开发常常让你头疼的是异步嵌套回调.
一层一层的大括号嵌套让你当场就迷失在回调函数中,更不用说隔一段时间自己去维护这段代码的时候,恨不能砸显示器来泄愤.
不过这个世界还是美好的,你遇到的问题大家也都遇到了,而且大牛们致力于减轻码农的各种node.js 的痛苦,开发出了高效好用的流程控制包,让你的逻辑变的清晰,维护起来一目了然.
常用的有 async ,step ,eventproxy
step本人接触很少,一直在用async,也是最近注意到了 eventproxy ,我想看看哪个性能更优,代码更加优雅,所以有了下面一个小demo.
这个demo不去比较实现原理,内部代码,而仅仅是从使用的角度,看一下在多个逻辑嵌套的情况下哪个速度稍微快一点,不太感兴趣的可以直接忽略下面内容.
首先需要引入 async 和 eventproxy npm包.
npm install async eventproxy
var EventProxy = require('eventproxy');
var async = require('async');
为了排除外界的干扰,我的逻辑测试代码没有涉及到网络请求,没有数据库,文件io处理,仅仅是一个消耗cpu的数学计算,我选择了 斐波纳契数列之和,很简单的数列,不明白的可以问度娘.
斐波纳契数列是这样的数列:1、1、2、3、5、8、13、21、……
数学模型上的实现方法是这样:F0=0,F1=1,Fn=F(n-1)+F(n-2)(n>=2)
数列的每个值是前面2个值的和.现在我们要求一个数值对应的斐波纳契数列之和,如果数值足够大,这个程序的递归迭代还是很耗cpu的.
代码实现如下:
function step1(n) {
if (n > 2)
return step1(n - 1) + step1(n - 2);
else
return 1;
}
在流程控制过程中,求一个数值的斐波纳契数列之和就表示一个业务逻辑处理,所以为了模拟一系列逻辑处理节点.
我需要给多个数值求斐波纳契数列,所以干脆我定义一个数组.
var len,val = [],
arr = [36, 39, 38, 31, 36,37,29,24,28,37,35,33,38,35,37];
len在后面会保存arr数组长度,val数组用来存放元素 斐波那契数列之和 .
上面的15个数字代表15次逻辑处理.
(一)用数组for循环15次逻辑处理,然后用计数器自动记录处理次数.最后返回保存斐波那契数列和的数组 val.
//for
console.time('for_time');
len = arr.length;
val = [];
console.log('------for循环测试开始------')
for (var i = 0; i < arr.length; i++) {
val.push(step1(arr[i]));
if (--len == 0) {
console.log(val);
console.timeEnd('for_time');
}
}
(二)async 用series串行无关联 和 parallel并行无关联 测试
async下的each /forEach 我随便测试了一下,下面主要看2种流程控制
//async 串行无关联
console.time('async_Series');
val = [];
console.log('------async_Series 测试开始------')
async.series([
function(done){
//逻辑处理
done(null,val.push(step1(arr[0])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[1])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[2])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[3])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[4])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[5])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[6])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[7])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[8])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[9])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[10])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[11])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[12])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[13])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[14])));
}
],function(error,result){
//最后结果
console.log(val);
console.timeEnd('async_Series');
});
//async 并行无关联
console.time('async_parallel');
val = [];
console.log('------async_parallel 测试开始------')
async.parallel([
function(done){
//逻辑处理
done(null,val.push(step1(arr[0])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[1])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[2])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[3])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[4])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[5])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[6])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[7])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[8])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[9])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[10])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[11])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[12])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[13])));
},
function(done){
//逻辑处理
done(null,val.push(step1(arr[14])));
}
],function(error,result){
console.log(val);
console.timeEnd('async_parallel');
});
(三)eventproxy 实现流程控制
flag1 到 flag15 手动输入好烦,有没有简单好用的方法.
//eventproxy
console.time('eventproxy');
val = [];
console.log('------eventproxy 测试开始------');
var ep = EventProxy.create("flag1", "flag2", "flag3","flag4","flag5","flag6", "flag7", "flag8","flag9","flag10","flag11", "flag12", "flag13","flag14","flag15",function (flag1, flag2,flag3,flag4,flag5,flag6, flag7,flag8,flag9,flag10,flag11, flag12,flag13,flag14,flag15) {
console.log(val);
console.timeEnd('eventproxy');
});
ep.emit("flag1", val.push(step1(arr[0])));
ep.emit("flag2", val.push(step1(arr[1])));
ep.emit("flag3", val.push(step1(arr[2])));
ep.emit("flag4", val.push(step1(arr[3])));
ep.emit("flag5", val.push(step1(arr[4])));
ep.emit("flag6", val.push(step1(arr[5])));
ep.emit("flag7", val.push(step1(arr[6])));
ep.emit("flag8", val.push(step1(arr[7])));
ep.emit("flag9", val.push(step1(arr[8])));
ep.emit("flag10", val.push(step1(arr[9])));
ep.emit("flag11", val.push(step1(arr[10])));
ep.emit("flag12", val.push(step1(arr[11])));
ep.emit("flag13", val.push(step1(arr[12])));
ep.emit("flag14", val.push(step1(arr[13])));
ep.emit("flag15", val.push(step1(arr[14])));
三种测试方法代码已经完成,下面就运行一下:
上面这一组测试结果很难看出问题,如果你把代码自己运行一下,你会看到所耗时间数据是很跳跃的,尤其像上面 async 的并行方法速度竟然比串行方法速度慢了一大截,一下感觉这个世界也不美好了.(上面的测试结果包含了async 的each 等运行结果,但是上面代码我去掉了这部分)
大概是因为这个密集型cpu运算,导致每个流程把cpu资源都消耗在了斐波那契数列之和的计算上,而真正逻辑处理的时候,由于逻辑处理也就15个,在cpu被计算全部占用后,流程控制上几乎无法比较了.所以这个结果很不稳定,一会eventproxy优于async ,一会又相反的结果.
所以打算让逻辑处理简单一点,不要把cpu压力全部集中在计算上,而让资源也充分体现在流程控制上.于是把上面的15个元素的数组改成了下面这样.
arr = [6, 13, 8, 10, 6,7,9,4,8,14,15,16,6,7,9];
还是15个,但是明显数值都很低.
然会进行下一轮测试:
虽然这么小的样本是无法说明一个结果的,但是在这个不靠谱的测试结果中,我们貌似看到了自己用for循环+计数器控制流程明显是耗时多一点. eventproxy 表现稳定,在这个测试表现最优,async表现有点不稳定 paraller 在 series名下没有表现出并行的优势.有待于更深层次的比较和一个大而全的样本来说服,这次的小测试就看做是一个熟悉代码的学习吧.