首先通過一個問題的出現以及解決,來介紹cc.AsyncPool的實現。
假設現在有個需求,就是一個頁面裏面要顯示很多圖片,而且這些圖片是需要下載的。如果我們的實現方法是顯示這個頁面的時候,一次性請求並創建這些圖片,這樣做可能會出現當我們要顯示這個頁面的時候,會出現卡了幾秒,然後圖片一次性顯示出來,顯然這種體驗是很差的。所以我們可能優化一下,當要顯示這個頁面時候先顯示背景,然後創建去請求一個圖片並創建成功後再去處理下一個。這裏的實現模式就用cc.AsyncPool異步池來實現。
通過上面的問題,我們先思考一下,我們實現的功能就是,別人傳一堆數據過來,我們先取出一個數據,然後交給迭代器且處理,同時迭代器在處理完成的時候通知給我們,在通知給我們的時候,我們繼續去下一個數據,當迭代器處理完成的數據等於原先總的數據的時候,就說明處理完成了。
所以我們實現一個異步池需要一下參數
1、迭代處理的對象srcObj(上面例子的所有圖片的遠程地址列表)
2、並行限制 limit,就是一次可以同時處理數量
3、迭代器(上面例子的單個圖片下載和創建控件流程)
4、全部處理後的回調 onEnd
5、上下文target(js的語法,修改函數的this指向)
下面是cc.AsyncPool帶註釋的代碼。
//+++++++++++++++++++++++++something about async begin+++++++++++++++++++++++++++++++
/**
* Async Pool class, a helper of cc.async
* @param {Object|Array} srcObj 迭代對象
* @param {Number} limit the limit of parallel(並行) number 並行限制
* @param {function} iterator 迭代器
* @param {function} onEnd 全部執行後的回調
* @param {object} target 迭代的上下文
* @constructor
*/
// async 異步
// 異步池大概流程:
// 記錄總的需要處理的數量
// 取出一個數據,當前工作的數量加1,並執行迭代器
//迭代器執行結束後,當前工作的數量減1,已完成的數據加1
// 如果已完成的數量等於需要處理的數據,表明全部處理完成,反之表面還沒全部處理結束,嘗試處理下一個數據
// 處理下一個數據時,如果當前工作的數據沒有超過最大併發數,且待處理的池還有數據,取下個數據處理
cc.AsyncPool = function (srcObj, limit, iterator, onEnd, target) {
var self = this;
// 是否執行後全部結束的回調
self._finished = false;
// 需要處理的數據
self._srcObj = srcObj;
// 同時併發的最大數量
self._limit = limit;
// 等待處理的數據池
self._pool = [];
self._iterator = iterator;
self._iteratorTarget = target;
self._onEnd = onEnd;
self._onEndTarget = target;
self._results = srcObj instanceof Array ? [] : {};
self._errors = srcObj instanceof Array ? [] : {};
// 把srcObj已index-value的元素壓入到數組_pool
cc.each(srcObj, function (value, index) {
self._pool.push({index: index, value: value});
});
// 總的數量
self.size = self._pool.length;
// 已經處理完成的數量,用於處理一個結束後判斷是否全部處理完成用
self.finishedSize = 0;
// 正在工作的數量
self._workingSize = 0;
self._limit = self._limit || self.size;
// 注入迭代器和其上下文
self.onIterator = function (iterator, target) {
self._iterator = iterator;
self._iteratorTarget = target;
};
// 注入結束回調和其上下文(這個是不是沒有用,和下面的onEnd衝突?)
self.onEnd = function (endCb, endCbTarget) {
self._onEnd = endCb;
self._onEndTarget = endCbTarget;
};
// 處理單個
self._handleItem = function () {
var self = this;
// 待處理池爲空或者 當前工作的數量已經達到限制
if (self._pool.length === 0 || self._workingSize >= self._limit)
return;
// 移除出一個
var item = self._pool.shift();
var value = item.value, index = item.index;
// 正在處理的數量加1
self._workingSize++;
// 運行迭代器,參數爲value,key,單個回調函數,異步池對象
self._iterator.call(self._iteratorTarget, value, index,
// 單個處理接口回調函數,參數爲err和接口
function (err, result) {
// 結束回調已經執行過
if (self._finished) {
return;
}
// 記錄每個數據處理的錯誤或者結果
if (err) {
self._errors[this.index] = err;
}
else {
self._results[this.index] = result;
}
// 已完成數量加1
self.finishedSize++;
// 正在運行數量減1
self._workingSize--;
// 完成數量等於總的數量(這裏可以去掉這個變量)
// 可以用當前正在工作的爲空,且異步池沒有對象了
// if (self._workingSize === 0 && self._pool.length === 0)
if (self.finishedSize === self.size) {
var errors = self._errors.length === 0 ? null : self._errors;
self.onEnd(errors, self._results);
return;
}
// 處理完一個後,處理下一個
self._handleItem();
}.bind(item),
self);
};
// 異步池創建成功後,調用這個開始處理數據
self.flow = function () {
var self = this;
if (self._pool.length === 0) {
if (self._onEnd)
self._onEnd.call(self._onEndTarget, null, []);
return;
}
// 一次性並行多個
for (var i = 0; i < self._limit; i++)
self._handleItem();
};
// 全部處理完成回調
self.onEnd = function(errors, results) {
self._finished = true;
if (self._onEnd) {
var selector = self._onEnd;
var target = self._onEndTarget;
// 清空引用
self._onEnd = null;
self._onEndTarget = null;
selector.call(target, errors, results);
}
};
};