短小強悍的JavaScript異步調用庫

原文連接:   7 lines JavaScript library for calling asynchronous functions 
翻譯人員: 鐵錨
翻譯時間: 2014年02月18日
示例地址: 簡短強悍的JavaScript異步調用庫在線調試

對於博文 20行完成一個JavaScript模板引擎 的備受好評我感到很驚訝,並決定用此文章介紹使用我常用的另外一個小巧實用的工具.咱們知道,在瀏覽器中的 JavaScript 絕大部分的操做都是異步的(asynchronous),因此咱們一直都須要使用回調方法,而有時難免陷入回調的泥淖而欲死欲仙.

假設咱們有兩個 functions ,咱們順序地在一個後面執行完後調用另外一個。他們都操做同一個變量。第一個設置它的值,第二個使用它的值。
var value;
var A = function() {
    setTimeout(function() {
        value = 10;
    }, 200);
}
var B = function() {
    console.log(value);
}
那麼,如今若是咱們運行 A();B(); 咱們將在控制檯看到輸出爲 undefined . 之因此會這樣是由於 A 函數使用了異步方式設置 value 。咱們能作的就是傳一個回調函數給A,並讓函數A在執行完後執行回調函數。
var value;
var A = function(callback) {
  setTimeout(function() {
    value = 10;
    callback();
  }, 200);
};
var B = function() {
  console.log(value);
};
 
A(function() {
  B();
});
這樣確實有用,但想象一下加入咱們須要運行5個或更多方法時將會發生什麼。一直傳遞迴調函數將會致使混亂和很是不雅觀的代碼。
好的解決辦法是寫一個工具函數,接受咱們的程序並控制整個過程。讓咱們先從最簡單的開始:
var queue = function(funcs) {
    // 接下來請看,董卿???
}
接着,咱們要作的是經過傳遞A和B來運行該函數 - queue([A, B])。咱們須要取得第一個函數並執行它。
var queue = function(funcs) {
    var f = funcs.shift();
    f();
}
若是執行這段代碼,您將看到一個 TypeError: undefined is not a function。這是由於 A函數沒收到回調參數但卻試圖運行它。讓咱們換一種調用方法。
var queue = function(funcs) {
    var next = function() {
        // ...
    };
    var f = funcs.shift();
    f(next);
};
在 A執行完後會調用 next 方法。將下一步操做放在 next 函數列表中是個很好的作法。咱們能夠將代碼歸攏在一塊兒,並且咱們可以傳遞整個數組(即使數組中有不少函數等待執行)。
var queue = function(funcs) {
    var next = function() {
        var f = funcs.shift();
        f(next);
    };
    next();
};
到了這一步,咱們基本上達到了咱們的目標。即函數A 執行後,接着會調用 B,打印出變量的正確值。這裏的關鍵是 shift 方法的使用。它刪除數組的第一個元素並返回該元素。一步一步執行下去, funcs數組就會變成 empty(空的)。因此,這可能會致使另外一個錯誤。爲了證實這一觀點,讓咱們假設咱們仍然須要運行這兩個功能,但咱們不知道他們的順序。在這種狀況下,兩個函數都應該接受回調參數(callback )並執行它。
var A = function(callback) {
    setTimeout(function() {
        value = 10;
        callback();
    }, 200);
};
var B = function(callback) {
    console.log(value);
    callback();
};
固然,咱們會獲得 TypeError: undefined is not a function
要阻止這一點,咱們應該檢查funcs數組是否爲空。
var queue = function(funcs) {
    (function next() {
        if(funcs.length > 0) {
            var f = funcs.shift();
            f(next);
        }
    })();
};
咱們所作的就是定義 next 函數並調用它。這種寫法減小了一點代碼。
讓咱們試着想象儘量多的狀況。好比當前執行功能的 scope 。函數內的 this 關鍵字可能指向了全球的 window 對象。,若是咱們能夠設置本身的scope 固然是件很酷的事情。
var queue = function(funcs, scope) {
    (function next() {
          if(funcs.length > 0) {
              var f = funcs.shift();
              f.apply(scope, [next]);
          }
    })();
};
咱們爲這個tiny 類庫增長了一個參數。接着咱們使用 apply  函數,而不是直接調用 f(next),來設置scope 並將參數 next 傳遞進去。一樣的功能,但漂亮多了。
咱們須要的最後一個特性,就是是函數間傳遞參數的能力。固然咱們不知道具體會有多少參數將被使用。這就是爲何咱們須要使用 arguments 變量的緣由。你可能知道,該變量在每一個 JavaScript函數中都是可用的,表明了傳進來的參數。它就和一個數組差很少,但不徹底是。由於在 apply 方法中咱們須要使用真正的數組,使用一個小竅門來進行轉換。
var queue = function(funcs, scope) {
    (function next() {
          if(funcs.length > 0) {
              var f = funcs.shift();
              f.apply(scope, [next].concat(Array.prototype.slice.call(arguments, 0)));
          }
    })();
};
下面是測試的代碼:
// 測試代碼
var obj = {
    value: null
};
 
queue([
    function(callback) {
        var self = this;
        setTimeout(function() {
            self.value = 10;
            callback(20);
        }, 200);
    },
    function(callback, add) {
        console.log(this.value + add);
        callback();
    },
    function() {
        console.log(obj.value);
    }
], obj);
執行後的輸出爲:
30
10
爲了代碼的可讀性和美觀,咱們將部分相關的代碼移到一行內:
var queue = function(funcs, scope) {
    (function next() {
          if(funcs.length > 0) {
              funcs.shift().apply(scope || {}, [next].concat(Array.prototype.slice.call(arguments, 0)));
          }
    })();
};
你能夠 點擊這裏查看並調試相關代碼 ,完整的測試代碼以下: 
var queue = function(funcs, scope) {
    (function next() {
          if(funcs.length > 0) {
              funcs.shift().apply(scope || {}, [next].concat(Array.prototype.slice.call(arguments, 0)));
          }
    })();
};


var obj = {
    value: null
};


queue([
    function(callback) {
        var self = this;
        setTimeout(function() {
            self.value = 10;
            callback(20);
        }, 200);
    },
    function(callback, add) {
        console.log(this.value + add);
        callback();
    },
    function() {
        console.log(obj.value);
    }
], obj);
相關文章
相關標籤/搜索