重构模式:用异步计算替代长计算
September 23rd, 2008
问题
当一个JavaScript计算进程耗时太长,浏览器会弹出警告框,不仅烦人而且一不小心就会点错导致后面的程序不被执行。
很多这类长计算是由于操作大数组造成的。虽然每个元素的处理耗时很少,当数组有几百上千个元素时整个计算时间就会长到超时。由于数组元素数不确定,对单一元素操作调优仍然不能根本解决问题。
办法
把长计算拆解成多个异步的短计算。
即使计算所需的总时间并不缩短,将阻塞的长计算拆解成异步的短计算至少带来响应性的提升:在计算进行的同时用户还可以做其他操作,而不是眼看着整个浏览器失去响应。
在拆解的基础上可以改进任务分发机制以提升整体计算时间。
步骤
- 重构原来的长计算
- 把整个长计算抽取到一个方法中
- 重构该方法使之呈现以下结构:
function longCalculation() {
before();
var items = getItems();
items.each(handleSingleItem);
after();
}
- 把循环重构为递归
- 把“操作数组中所有元素”的逻辑抽取到一个递归调用的方法,使之呈现以下结构:
function handleItems(items, cur) {
if(cur == items.length - 1) {
after();
return;
}
var item = items[cur];
handleSingleItem(item);
handleItems(items, cur + 1);
}
function longCalculation() {
before();
var items = getItems();
handleItems(items, 0);
}
- 把针对每个元素的操作变成一个异步的计算进程──这一步很简单,只要在调用handleItems方法时始终加上delay就行。
以上。应该很容易写出一个类似于library的玩意来封装这个模式。太容易了以至于我不想写。




September 24th, 2008 at 11:53 AM 学erlang? js没有优化过这部分,会不会栈溢出?
September 24th, 2008 at 12:03 PM 学erlang? js没有优化过这部分,会不会栈溢出?
September 27th, 2008 at 09:30 AM 这种密集计算的东西,resig在他的新书《javascript忍者秘诀》里面专门讲到了,可以利用setTimeout(也就是你说的delay)分批执行来避免浏览器假死…… 其实在日常开发中可以知道,纯js数组操作没什么问题,最耗时的还是dom遍历、处理,我们可以在这些情况下利用这种“伪”异步模式。