来自
http://blog.jcoglan.com/2010/08/30/the-potentially-asynchronous-loop/
(可能已被墙)
写程序时候经常会碰到这种情况,有一个队列,对这个队列进行一次操作会相当消耗性能,因此需要对队列中每个元素独立进行操作,一个接一个。如果这个操作是阻塞的话 用一个for循环就搞定了,如果是异步操作呢。
比如,有一个url数组,需要轮流访问每一个url,上一个返回后才进行下一个
listOfUrls.forEach(function(url) {
$.get(url, function(response) {
// handle response
});
});
以上代码显然是不满足要求的,所有请求将一次发送出去,而不是一个接一个。下面介绍这样的异步forEach:
Array.prototype.asyncEach = function(iterator) {
var list = this,
n = list.length,
i = - 1;
var resume = function() {
i += 1;
if (i === n) return;
iterator(list[i], resume);
};
resume();
};
以上代码展示了这种异步模型,对于先前url数组的例子,就可以这样:
listOfUrls.asyncEach(function(url, resume) {
$.get(url, function(response) {
// handle response
resume();
});
});
非常漂亮,不是吗。对于异步操作来说,上面代码是没有问题的,但如果其中某些操作是同步的,在resume和iterator这两个函数不停地互相调用可能会造成堆栈溢出,解决方法是用setTimeout来迭代:
Array.prototype.asyncEach = function(iterator) {
var list = this,
n = list.length,
i = - 1;
var iterate = function() {
i += 1;
if (i === n) return;
iterator(list[i], resume);
};
var resume = function() {
setTimeout(iterate, 1);
};
resume();
};
setTimeout会把每次迭代操作的调用堆栈独立开,这样就不会溢出了。
ps:虽然以上setTimeout的时间参数设为0毫秒,还会有10ms左右的延时,100次迭代就会造成1秒左右的延时,对一些苛刻要求时间的应用是一个浪费,解决方法是用setZeroTimeout代替setTimeout
这是下一篇介绍的 setZeroTimeout