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); } }())
你可以做很多事情:
- 优化循环 – 如果繁重的工作与DOM访问有关,请参阅此答案
- 如果函数正在使用某种原始数据types的数组MSDN MDN
-
setTimeout()方法称为eteration 。 非常有用。
-
这个函数对于非函数式编程语言来说似乎是非常直接的。 JavaScript获得callback优势的问题 。
-
一个新function是networking工作者MDN MSDN 维基百科 。
-
最后一件事(可能)是将所有方法组合在一起 – 与传统的函数只使用一个线程的方式相结合。 如果你可以使用networking工作者,你可以把工作分成几个部分。 这应该尽量减less完成任务所需的时间。