前言
在日常开发中,也许我们会遇到这样的一个问题。我们利用【发布订阅模式】(如果不了解的可以直接访问此链接www.cnblogs.com/xiaoxiaokun… )去执行【发布】事件时,遇到函数内部涉及到异步执行时,就比较难以处理。为了满足这种需求,我专门写了一个这样的插件用于函数整合队列并顺序执行。
函数队列循环执行
/** *1.0.0.1版本 */ var list=[];//存储函数 list.push(function(){ console.log(1); }); list.push(function(){ console.log(2); }); list.push(function(){ console.log(3); }); for (var i=0;fn=list[i++];) { fn();//执行 }
结果:1,2,3
这是最简单的方式,可以实现函数整合成一个队列,按照先进先出顺序执行。现在在仔细看发现,如果函数中有异步执行那函数执行就不能保证按照顺序执行,例如:
var list=[];//存储函数 list.push(function(){ console.log(1); }); list.push(function(){ setTimeout(function() { console.log(2); }, 2000); }); list.push(function(){ console.log(3); }); for (var i=0;fn=list[i++];) { fn();//执行 }
输出的结果肯定是 1,3,2
/** *1.0.0.2版本 */ var list=[]; list.push(function(){ console.log(1); }); list.push(function(){ console.log(2); }); list.push(function(){ console.log(3); }); function start(list){ if(list.length){ list.shift()(); arguments.callee(list); } } start(list);
这种方式可以等到执行完毕,清除list内部执行过后的函数。不影响下次push 执行。但是异步函数还是未解决。
/** *1.0.0.3版本 */ var list=[];//存储数组的集合 list.push(function(){ console.log(1); }); list.push(function(callback){ //callback是代表这个函数是异步的函数 setTimeout(function() { console.log(2); callback();//执行完异步函数执行回调函数 }, 2000); }); list.push(function(){ console.log(3); }); function start(){ //判断数组的长度 if(list.length){ var fn=list.shift();//取出数组第一个函数 //判断函数是否带有参数 if(fn.length){ fn(start);//执行该函数,并且把 start本身传递进去。 }else{ fn(); start(); } } } start();
此版本可以解决带有异步执行的函数按照刚开始push进去的顺序依次执行。
需要注意的是,如果函数是内部带有异步执行的函数,需要传递一个参数来告诉start。但是如果我们push进去的函数本身有好多个参数这需要怎么办呢!!接下来看另一版本。
/** *1.0.0.4版本 */ var list=[];//存储数组的集合 list.push(function(){ console.log(1); }); list.push(function(callback){ setTimeout(function() { console.log(2); callback(); }, 2000); }); list.push(function(){ console.log(3); }); function start(){ //判断数组的长度 if(list.length){ var fn=list.shift();//取出数组第一个函数 //判断函数是否带有参数 if(fn.length && getfunarg(fn)[0]=='callback'){ fn(start);//执行该函数,并且把 start本身传递进去。 }else{ fn(); start(); } } } start(); /** * 查找函数参数名 * @fn {Function } 要查找的函数 * @return []返回参数数组 * */ function getfunarg(fn) { var f = /^[s(]*function[^(]*(s*([^)]*?)s*)/.exec(fn.toString()); return f && f[1] ? f[1].split(/,s*/) : []; }
到现在为止,我们这几个函数基本已经满足我们的需求,但是push的时候,假设函数多个参数,我们还需进一步优化代码!为了把这个插件做的更好。我决定还是把callback放在最后,这样就能保证函数传递参数不受影响。
最终版本
/** * 作者:小小坤 * 联系:java-script@qq.com * 日期:2017-11-11 * 版本:1.0.0.4 * -----------使用说明---------- * 1、把所有函数【包含异步执行的函数】按照顺序依次 使用lk.push存入 * 2、带有参数的函数,一定要注意{最一个参数如果是callback}会被认为是 异步执行函数 * 3、异步执行的函数,需要把最一个参数设置为callback。并在函数执行完毕执行callback();函数保证按照顺序执行 * * */ ;! function() { var list = [], //存储函数的列表 isFun = Object.prototype.toString; //用于验证是否是函数 /** * 添加到列表中 * @fn {Function} 函数体 * */ function push(fn) { isFun.call(fn) === "[object Function]" && list.push(fn); }; /** * 开始执行列表中的所有函数, * 按照先进先出原则 * * */ function star() { if(list.length) { var fn = list.shift(),//截取第一个函数 arry=getfunarg(fn),//获取这个函数的参数列表 _length=arry.length;//参数列表的长度 if(_length && arry[_length-1] === 'callback') { if(_length===1){ fn(star); }else{ arry.pop();//删除最后一个参数 arry.push(star);//把回调函数存入数组 fn.apply(this,arry); } } else { fn.apply(this,arry); star(); } } } /** * 查找函数参数名 * @fn {Function } 要查找的函数 * @return []返回参数数组 * */ function getfunarg(fn) { var f = /^[s(]*function[^(]*(s*([^)]*?)s*)/.exec(fn.toString()); return f && f[1] ? f[1].split(/,s*/) : []; } //挂在到Windows上。 window.lk = { push: push, start: star } }(); //使用测试 /**--------一条华丽的分割线--------**/ var a=100, b=200, d=300, f=400; //定义函数 a2 ,此函数带有一个参数,被认为是异步函数 function a2(a,b,callback) { console.time('2'); setTimeout(function() { console.timeEnd('2'); callback(); console.log(a,'a'); }, 1000); } //把函数函数 a2 放入数组 lk.push(a2); //定义函数 a3 function a3(d, f) { console.log(f,'f'); console.log(3); } //把函数函数 a3 放入数组 lk.push(a3); //定义函数 a4 此函数带有一个参数,被认为是异步函数 function a4(callback) { console.time('4'); setTimeout(function() { console.timeEnd('4'); callback(); }, 2000); } //把函数函数 a4 放入数组 lk.push(a4); //最后开始执行 lk.start();
最终此插件完成,需要压缩的同学可以自行压缩。代码比较简单,提供了两个方法。
push存储函数列表
start开始执行
总结
通过上边的代码编写我们学到了处理函数列表时候,需要考虑到异步函数。处理异步函数,需要回调函数参与。这样就能帮助代码按照顺序执行。