为什么setTimeout拆分的工作负载仍会冻结浏览器?

时间:2012-06-08 12:29:46

标签: javascript v8

http://jsfiddle.net/Codemonkey/ye5qv/2/

我正在尝试使用setInterval编写大型工作负载,以便浏览器可以在每次迭代工作之间捕获它。在上面的例子中,我给浏览器在斐波那契计算的每个间隔之间休息了100毫秒,但是从调用fib()的那一刻起到调用回调的那一刻,浏览器仍然处于冻结状态。 注意:在我的示例中,在文档加载后一秒钟之前没有调用fibo函数,因此您可以清楚地看到它冻结几秒钟

究竟为什么这种方法不起作用,我怎样才能使它工作?或者,哪种其他方法可以达到相同的目标结果?重申一下,使用setInterval拆分工作的目的是浏览器在此过程中 永远不会 冻结或编译器。

我正在使用Chrome,我主要关注的是V8引擎,但x-browser兼容的解决方案是一个奖励

3 个答案:

答案 0 :(得分:4)

不幸的是,JavaScript setIntervalsetTimeout并非真正异步,因此所有内容仍将在单个线程中运行。这意味着当调用setInterval回调时,它会在主线程中运行并阻止其他所有内容的执行。

以下是John Resig的精彩文章,详细讨论了这一点:How JavaScript Timers Work

如果您想要真正的异步JavaScript,则必须使用类似Web Workers的内容。


正如其他人在此指出的那样,在你的特定情况下,你实际上在这一行上有一个无限循环:

for(var i = start; i < i+workload; i++)

导致您的代码:

setTimeout(function() {
    processor(start + workload);
}, 100);

永远不会被召唤。有关详细信息,请参阅Guffa's excellent answer

我的答案的其余部分仍然适用 - 如果您尝试做的不仅仅是简单的操作,同时为.gif设置动画,您可能仍然会在没有像Web Workers这样的东西的情况下冻结或口吃。

答案 1 :(得分:4)

问题是你的代码永远不会达到调用setTimeout来控制回浏览器的程度。

改变这个:

for(var i = start; i < i+workload; i++)

为:

for(var i = start; i < start+workload; i++)

由于workload是一个正数,i < i + workload永远不会为假,所以你有一个无限循环。


在将控制权交还给浏览器时,您不必等待100毫秒,只需调用setTimout即可使浏览器处理事件,因此您可以使用时间0:

setTimeout(function() {
  processor(start + workload);
}, 0);

演示:http://jsfiddle.net/Guffa/ye5qv/3/

答案 2 :(得分:1)

我注意到你的代码中setInterval实际上从未被调用过,因为在这行中

for(var i = start; i < i+workload; i++)

条件始终为真,因为i始终小于i+workload,因此它永远不会离开循环。从循环内部的函数返回的好东西。