未优化之前的版本:
let i = 0;
let start = Date.now();
function count() {
// do a heavy job
for (let j = 0; j < 1e9; j++) {i++;}
alert("Done in" + (Date.now() - start) + 'ms');
}
count();
上述 count 函数里的 for 循环的 i 累加,是一个 CPU 密集型工作,在执行结束之前,JavaScript 引擎工作队列里的其余工作,没有机会失去执行。
优化版本
将 i 从 1 累加到 1e9 的工作,拆解成 1000 个小的子工作。每个子工作执行结束之后,调用 setTimeout
调度本身,这样工作队列里其余工作有机会失去执行。
let i = 0;
let start = Date.now();
function count() {// do a piece of the heavy job (*)
do {i++;} while (i % 1e6 != 0);
if (i == 1e9) {alert("Done in" + (Date.now() - start) + 'ms');
} else {setTimeout(count); // schedule the new call (**)
}
}
count();
- 第一轮工作执行:i=1…1000000
- 第二轮工作执行:i=1000001..2000000
以此类推。
当初,如果在引擎忙于执行第 1 局部时呈现新的辅助工作(例如 onclick 事件),它会排队,而后在第 1 局部实现时执行,而后再执行下一部分。在 count 执行之间,应用 setTimeout
定期返回事件循环,为 JavaScript 引擎提供了调度执行其余工作的机会,以对其余用户操作做出反馈。
let i = 0;
let start = Date.now();
function count() {
// move the scheduling to the beginning
if (i < 1e9 - 1e6) {setTimeout(count); // schedule the new call
}
do {i++;} while (i % 1e6 != 0);
if (i == 1e9) {alert("Done in" + (Date.now() - start) + 'ms');
}
}
count();
为浏览器脚本拆分 CPU 密集型工作的另一个益处是咱们能够显示进度批示。
如前所述,只有在以后运行的工作实现后才会绘制对 DOM 的更改,无论以后运行的工作须要多长时间能力执行结束。
上面是一个例子:
<div id="progress"></div>
<script>
function count() {for (let i = 0; i < 1e6; i++) {
i++;
progress.innerHTML = i;
}
}
count();
</script>
对 i 的更改要等到函数执行实现后才会显示,所以咱们只会看到最初一个值。
但咱们也可能想在工作期间展现一些货色,例如一个进度条。
如果咱们应用 setTimeout 将沉重的工作拆分为多个局部,那么 i 会在多个局部执行之间绘制进去。
<div id="progress"></div>
<script>
let i = 0;
function count() {// do a piece of the heavy job (*)
do {
i++;
progress.innerHTML = i;
} while (i % 1e3 != 0);
if (i < 1e7) {setTimeout(count);
}
}
count();
</script>