我的JS脚本中有这样的功能:
function heavyWork(){
for (i=0; i<300; i++){
doSomethingHeavy(i);
}
}
也许“doSomethingHeavy”本身就可以,但重复300次会导致浏览器窗口停留不可忽略的时间。在Chrome中,这不是一个大问题,因为只有一个Tab受到影响;但对于Firefox来说,这是一场彻底的灾难。
有没有办法告诉浏览器/ JS“放轻松”并且不会在调用doSomethingHeavy之间阻止所有内容?
答案 0 :(得分:19)
您可以将来电嵌套在setTimeout
电话中:
for(...) {
setTimeout(function(i) {
return function() { doSomethingHeavy(i); }
}(i), 0);
}
这会将对doSomethingHeavy
的调用排队等待立即执行,但其他JavaScript操作可以嵌入它们之间。
更好的解决方案是让浏览器通过Web Workers生成一个新的非阻塞进程,但这是HTML5特有的。
修改强>
使用setTimeout(fn, 0)
实际上需要比零毫秒更长的时间 - 例如,Firefox enforces a minimum 4-millisecond wait time。更好的方法可能是使用 setZeroTimeout ,它更喜欢postMessage
进行即时,可中断的函数调用,但使用setTimeout
作为旧浏览器的后备。
答案 1 :(得分:11)
您可以尝试在setTimeout
中包装每个函数调用,超时为0.这会将调用推送到堆栈的底部,并且应该让浏览器在每个函数之间休息。
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);
}
}
答案 2 :(得分:7)
答案 3 :(得分:3)
我们需要经常向浏览器释放控制权,以避免垄断浏览器的注意力。
释放控制权的一种方法是使用setTimeout
,它会安排在某段时间内调用“回调”。例如:
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
是不够的,好像它是魔法般的小精灵:你真的需要在回调中封装其余的计算。通常情况下,setTimeout
将是函数中的最后一项,其余的计算将填充到回调函数中。
对于您的特定情况,代码需要仔细转换为以下内容:
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
上向浏览器释放控制权。
作为另一个具体示例,请参阅:How can I queue a series of sound HTML5 <audio> sound clips to play in sequence?
高级JavaScript程序员需要知道如何进行此程序转换,否则他们会遇到您遇到的问题。你会发现,如果你使用这种技术,你将不得不以一种特殊的风格编写你的程序,其中每个可以释放控制的函数都有一个回调函数。这种风格的技术术语是“延续传递风格”或“异步风格”。
答案 4 :(得分:2)
我看到两种方式:
a)您可以使用Html5功能。然后你可以考虑使用工作线程。
b)你拆分这个任务并排队一条消息,这个消息只需要一次调用一次,并且只要有事情可以进行迭代。
答案 5 :(得分:2)
你可以做很多事情:
答案 6 :(得分:1)
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);
}
}())
答案 7 :(得分:1)
有一个人写了一个特定的backgroundtask javascript库来做这么繁重的工作..你可以在这里查看这个问题:
Execute Background Task In Javascript
我没有使用它,只是使用了提到的线程用法。