js函式整合佇列順序執行外掛

小小坤發表於2017-11-12

前言

在日常開發中,也許我們會遇到這樣的一個問題。我們利用【釋出訂閱模式】(如果不瞭解的可以直接訪問此連結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開始執行

總結

通過上邊的程式碼編寫我們學到了處理函式列表時候,需要考慮到非同步函式。處理非同步函式,需要回撥函式參與。這樣就能幫助程式碼按照順序執行。

相關文章