首先通过一个问题的出现以及解决,来介绍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);
}
};
};