js函数整合队列顺序执行插件

1,814 阅读4分钟

前言

在日常开发中,也许我们会遇到这样的一个问题。我们利用【发布订阅模式】(如果不了解的可以直接访问此链接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开始执行

总结

通过上边的代码编写我们学到了处理函数列表时候,需要考虑到异步函数。处理异步函数,需要回调函数参与。这样就能帮助代码按照顺序执行。