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

Why does a workload split up by setTimeout still freeze the browser?

本文关键字:冻结 浏览器 负载 工作 setTimeout 拆分 为什么      更新时间:2023-09-26

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

我正在尝试使用 setInterval 编写大型工作负载以重复运行,以便浏览器可以在每次工作迭代之间喘口气。在上面的示例中,我在每个斐波那契计算间隔之间给浏览器 100 毫秒的休息时间,但从调用fib()到调用回调的那一刻,浏览器仍然被冻结。注意:在我的示例中,fibo函数在文档加载后一秒之前不会调用,因此您可以清楚地看到它冻结了几秒钟

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

正在使用 chrome,我主要关注 V8 引擎,但与 x 浏览器兼容的解决方案是一个奖励

问题是你的代码永远不会达到它调用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/

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

这是 John Resig 的一篇很棒的文章,详细讨论了这个问题:JavaScript Timers 如何工作。

如果你想要真正的异步JavaScript,你必须使用像Web Workers这样的东西。


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

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

这导致了您的代码:

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

永远不要被召唤。请参阅Guffa的出色答案以获取更多信息。

不过,我的其余答案仍然适用 - 如果您尝试做的不仅仅是简单的操作和同时对.gif进行动画处理,那么如果不使用 Web Workers 之类的东西,您可能仍然会出现冻结或卡顿。

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

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

条件总是正确的,因为i总是小于i+workload,所以它永远不会退出循环。从循环内的函数返回是件好事。