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

我在我的JS脚本中有这样的function:

function heavyWork(){ for (i=0; i<300; i++){ doSomethingHeavy(i); } } 

也许“doSomethingHeavy”本身就可以,但重复300次会导致浏览器窗口卡住一个不可忽视的时间。 在Chrome中,这不是一个大问题,因为只有一个Tab是有效的; 但对于Firefox来说却是一场完全的灾难。

有什么办法告诉浏览器/ JS“放松一下”,而不是阻止之间的一切调用doSomethingHeavy之间的一切?

你可以将你的调用嵌套在setTimeout调用中:

 for(...) { setTimeout(function(i) { return function() { doSomethingHeavy(i); } }(i), 0); } 

这将调用doSomethingHeavy来立即执行,但其他JavaScript操作可以在它们之间被楔入。

一个更好的解决scheme是实际上浏览器通过Web Workers产生一个新的非阻塞过程,但这是特定于HTML5的。

编辑:

使用setTimeout(fn, 0)实际上需要比零毫秒更长的时间 – 例如,Firefox 强制执行至less4毫秒的等待时间 。 一个更好的方法可能是使用setZeroTimeout ,它使用postMessage来实现即时的,可中断的函数调用,但是使用setTimeout作为旧版浏览器的后备。

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

 function heavyWork(){ for (i=0; i<300; i++){ setTimeout(function(){ doSomethingHeavy(i); }, 0); } } 

编辑:我只是意识到这将无法正常工作。 每个循环迭代的i值都是相同的,你需要做一个闭包。

 function heavyWork(){ for (i=0; i<300; i++){ setTimeout((function(x){ return function(){ doSomethingHeavy(x); }; })(i), 0); } } 

您需要使用Web Workers

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

如果你在谷歌search周围有很多networking工作者的链接

为了避免垄断浏览器的注意力,我们需要每隔一段时间向浏览器释放控制权。

一种释放控制的方法是使用setTimeout ,它调度在某个时间段被调用的“callback”。 例如:

 var f1 = function() { document.body.appendChild(document.createTextNode("Hello")); setTimeout(f2, 1000); }; var f2 = function() { document.body.appendChild(document.createTextNode("World")); }; 

在这里调用f1会将hello添加到文档中,计划待处理的计算,然后将控制权释放给浏览器。 最终, f2将被调用。

请注意,在整个程序中散布setTimeout是不够的,就像它是魔法小精灵一样:你真的需要在callback中封装其余的计算。 通常情况下, setTimeout将是函数中的最后一个东西,其余的计算填充到callback函数中。

对于你的具体情况,代码需要仔细转换成这样的东西:

 var heavyWork = function(i, onSuccess) { if (i < 300) { var restOfComputation = function() { return heavyWork(i+1, onSuccess); } return doSomethingHeavy(i, restOfComputation); } else { onSuccess(); } }; var restOfComputation = function(i, callback) { // ... do some work, followed by: setTimeout(callback, 0); }; 

这将在每次restOfComputation上释放对浏览器的控制restOfComputation

作为另一个具体的例子,请参阅: 我如何排列一系列声音的HTML5audio片段才能顺序播放?

高级JavaScript程序员需要知道如何做这个程序转换,否则他们遇到了你遇到的问题。 你会发现,如果你使用这种技术,你将不得不以独特的风格编写你的程序,每个可以释放控制的函数都需要一个callback函数。 这种风格的技术术语是“延续传球风格”或“asynchronous风格”。

我看到两种方式:

a)您可以使用Html5function。 那么你可以考虑使用工作线程。

b)你分解这个任务,并且排队一个只需要一次调用的消息,只要有事情要做就迭代。

有一个人写了一个特定的backgroundtask JavaScript库来做这么繁重的工作..你可以在这里查看这个问题:

在Javascript中执行后台任务

没有用过我自己,只是用了也提到的线程用法。

 function doSomethingHeavy(param){ if (param && param%100==0) alert(param); } (function heavyWork(){ for (var i=0; i<=300; i++){ window.setTimeout( (function(i){ return function(){doSomethingHeavy(i)}; })(i) ,0); } }()) 

你可以做很多事情:

  1. 优化循环 – 如果繁重的工作与DOM访问有关,请参阅此答案
    • 如果函数正在使用某种原始数据types的数组MSDN MDN
  2. setTimeout()方法称为eteration 。 非常有用。

  3. 这个函数对于非函数式编程语言来说似乎是非常直接的。 JavaScript获得callback优势的问题

  4. 一个新function是networking工作者MDN MSDN 维基百科

  5. 最后一件事(可能)是将所有方法组合在一起 – 与传统的函数只使用一个线程的方式相结合。 如果你可以使用networking工作者,你可以把工作分成几个部分。 这应该尽量减less完成任务所需的时间。