浅析数组分时处理
Time: 09-11-29 Comments: 2
终于开始写这篇文章了, 憋了很久, 故意等到写完JS的单线程和计时器, 才开始着手准备这一篇.
因为, 我觉得要把分时处理的妙处理解得深刻一些, 就必须先理解”JS的单线程和计时器”!
一. 背景
有时, 我们需要用JS处理大量的数据, 比如常见的生成好友列表: 从接口取得数据后, 经过大量运算生成一大段html, 然后写入某个容器中, 这样的操作很容易让浏览器(尤其是IE6)出现挂起现象(在JS执行时,页面中所有的更新操作暂停), 或者等待较长时间后, 才看到内容被写入(如果运行了至少5秒钟还没有停止,一些浏览器如Firefox, Opera将产生一个提示框警告用户脚本无相应).
二. 牛人的解决方案
国外牛人以处理一个大数组为例, 为我们提供了一种解决方案(此方案也有几次改进, 我们一起感受一下他的改进过程, 会收获不少哦):
1. 方案v1版
function chunk(array, process, context){
var items = array.concat(); // 复制数组array, 奇技淫巧啊
setTimeout(function(){
var item = items.shift(); // 取出数组的第一项
process.call(context, item); // 逐项处理
if (items.length > 0) {
setTimeout(arguments.callee, 100);
}
}, 100);
}
a. 分析
(1) Jakob Nielsen的研究表明, 100ms是一个让用户感觉系统还在立即响应的临界值. 这就意味着,某段JS的执行时间不能超过100ms, 因为, 当JS还在执行时, UI是无法更新的, 如果超过100ms之后, UI再去更新, 用户会感觉到浏览器响应缓慢;
(2) 传统的处理方式: 以创建好友列表为例, 等到将所有数据格式化成html后, 再写入容器中,当数据量较大时, 这个处理所需的时间很可能会超过100ms;
(3) 作者享用了上面的研究成果: 抛弃传统的处理方式, 改为对数组项进行逐个处理(注:这应该是一种分块处理的思想, 把一个大块分成几个小块, 然后对小块进行逐块处理), 每处理完一个, 就更新一次UI(作者的code中并未写出, 但我想这个操作应该是放在了process中),且每两次处理之间会有100ms的延迟, 过程如下图所示:
b. 该方案的缺点
(1) 由于每处理完一项, 就会延时100ms, 如果数组项比较大, 那么处理完所有项需要花费较多的时间, 比如数组有100项, 则处理完所有项, 至少需要10s(至少10s, 是因为process执行需要时间,只有当process执行完毕后, 才会开始下一次延时), 虽然UI在不断更新, 但这会延长其他排队者的等候时间.
(2) 上面的代码并未限制process的执行时间, 如果某次process的执行时间超过了100ms, 那么,还是会出现UI更新缓慢的情况;
(3) 延时100ms, 时间有点长了;
2. 方案v2版
function timedChunk(items, process, context, callback){
var todo = items.concat(); // 复制数组
setTimeout(function(){
var start = +new Date(); // 返回当前时间戳, 奇技淫巧啊
do {
process.call(context, todo.shift());
} while (todo.length > 0 && (+new Date() - start < 50));
if (todo.length > 0) {
setTimeout(arguments.callee, 25);
} else {
callback(items);
}
}, 25);
}
a. 分析
(1) v1版的方案, 延时设置得有点长, 作者推荐设置为25ms(考虑到了浏览器的最小延迟时间);
(2) 经作者的研究发现, 在web浏览器中, 临界时间应该为50ms;
(3) 在该版中, 作者使用了一个do…while处理, while的判断条件非常经典, 50ms的限制被应用在这里, do…while的作用, 我是这样理解的:
当数组长度大于0, 且处理当前项的时间在50ms内时, 就接着处理下一项;
当数组长度大于0, 但处理当前项的时间超过50ms时, 就先退出do…while循环, 此时, 若数组项还未处理完毕, 则25ms之后, 会接着处理余下的数组项.
(4) 过程如下图所示:
3. 两种方案的比较
a. 性能
据作者测试, 在处理同一数组时(500项), 方案2耗时不到方案1的10%.
b. 时间差在哪里
(1) 对于方案1, 延时较长, 且延时的次数是固定的,等于数组长度;
(2) 对于方案2, 延时较短, 且延时的次数是不确定的, 最少有1次(数组项在do…while的时候,一次处理完毕, 没有超过50ms的), 最多时也才和方案1一样(每个数组项的处理都超过50ms, 即便是这样, 每次延时也少了75ms).
Leave a comment!
关于计时器 >>

Comments
释然 at 2009年12月7日 8:09 上午
顶~这个小虎他们在店铺分类里有应用~
paper at 2009年12月29日 4:36 下午
function asyncInnerHTML(HTML, callback) {
var temp = document.createElement(‘div’),
frag = document.createDocumentFragment();
temp.innerHTML = HTML;
(function(){
if(temp.firstChild){
frag.appendChild(temp.firstChild);
setTimeout(arguments.callee, 0);
} else {
callback(frag);
}
})();
}
这段代码也很不错。
from:http://james.padolsey.com/javascript/asynchronous-innerhtml/