Javascript - 如何在繁重的工作中避免阻止浏览器?

Gad*_*i A 19 javascript

我的JS脚本中有这样的功能:

function heavyWork(){
   for (i=0; i<300; i++){
        doSomethingHeavy(i);
   }
}
Run Code Online (Sandbox Code Playgroud)

也许"doSomethingHeavy"本身是好的,但它重复300次导致被卡住了不可忽略的时间浏览器窗口.在Chrome中,这不是一个大问题,因为只有一个Tab受到影响; 但对于Firefox来说,这是一场彻底的灾难

有没有办法告诉浏览器/ JS"放轻松",而不是阻止对doSomethingHeavy的调用之间的所有内容?

aps*_*ers 21

您可以将呼叫嵌套在setTimeout呼叫中:

for(...) {
    setTimeout(function(i) {
        return function() { doSomethingHeavy(i); }
    }(i), 0);
}
Run Code Online (Sandbox Code Playgroud)

这会将调用排队doSomethingHeavy等待立即执行,但其他JavaScript操作可以嵌入它们之间.

更好的解决方案是让浏览器通过Web Workers生成一个新的非阻塞进程,但这是HTML5特有的.

编辑:

setTimeout(fn, 0)实际使用时间比零毫秒长 - 例如,Firefox 强制执行至少4毫秒的等待时间.更好的方法可能是使用setZeroTimeout,它更喜欢postMessage瞬时的,可中断的函数调用,但setTimeout用作旧版浏览器的后备.

  • 干得好.我认为值得注意的是浏览器将在`doSomethingHeavy`期间被冻结,所以如果有长操作,仍会有挂起,但它将是1/300的时间. (3认同)

Roc*_*mat 11

您可以尝试在a中包装每个函数调用,setTimeout超时为0.这会将调用推送到堆栈的底部,并且应该让浏览器在每个函数之间休息.

function heavyWork(){
   for (i=0; i<300; i++){
        setTimeout(function(){
            doSomethingHeavy(i);
        }, 0);
   }
}
Run Code Online (Sandbox Code Playgroud)

编辑:我刚刚意识到这不起作用.i对于每个循环迭代,该值都是相同的,您需要进行闭包.

function heavyWork(){
   for (i=0; i<300; i++){
        setTimeout((function(x){
            return function(){
                doSomethingHeavy(x);
            };
        })(i), 0);
   }
}
Run Code Online (Sandbox Code Playgroud)

  • @Teemu:根据[spec](http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#timers),这应该自动发生:`如果当前正在运行的任务是由setTimeout()方法创建的任务,超时小于4,然后将超时增加到4`. (2认同)

Con*_*Del 8

您需要使用Web Workers

https://developer.mozilla.org/En/Using_web_workers

如果你在谷歌搜索,网络工作者有很多链接