2012-06-08 45 views
1

http://jsfiddle.net/Codemonkey/ye5qv/2/爲什麼由setTimeout分割的工作量仍會凍結瀏覽器?

我正在嘗試編寫大量的工作負載來重複使用setInterval,以便瀏覽器可以在每次迭代工作之間捕獲它的呼吸。在我上面的示例中,我給瀏覽器在斐波那契計算的每個間隔之間保持100ms的休息時間,但從fib()被調用到調用回調的那一刻起,瀏覽器仍然被凍結。 注:在我的例子中,FIBO功能沒有文件裝載後,前一秒,所以你可以清楚地看到它凍結了幾秒鐘

究竟爲什麼不是這種方法的工作調用,哪能讓它起作用?或者,其他方法可以達到相同的目標結果?重申一下,使用setInterval來分割工作的目標是瀏覽器應該在過程中凍結或分析。

我使用鉻,我主要關心的是V8引擎,但一個X的瀏覽器兼容的解決方案是一個獎金

+0

我希望這只是一個例子。你知道Binet的斐波那契數字公式嗎? – MaxArt

+0

是的,fibo函數的唯一一點是創建一個工作負載**將**導致瀏覽器凍結,除非我的方法工作 – Hubro

+0

當工作負載開始花費更長的時間然後100毫秒時,您可能會看到重新輸入問題。請改用setTimeout,並在每次完成工作時重置它。編輯 - 我看到小提琴使用setTimeout,但問題標題說setInterval。這是什麼? – asawyer

回答

4

問題是,您的代碼永遠無法達到調用setTimeout的位置以將控制權交還給瀏覽器。

更改此:

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

到:

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

作爲workload爲正數,i < i + workload永遠不會是假的,讓你擁有一個無限循環。


你不必放棄時控制返回給瀏覽器,只是打電話setTimout等待100毫秒就足以使瀏覽器處理事件,這樣你就可以使用的時間0:

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

演示:http://jsfiddle.net/Guffa/ye5qv/3/

+0

我知道我不需要100ms,但當「甚至100ms還不夠」時,我非常驚駭。雖然我沒有注意到循環錯誤,但是謝謝 – Hubro

+0

+1:這比我的回答要好,我沒有注意到無限循環 - 好的結果。我深入瞭解JS中真正的異步操作的細節。 –

+0

如果你閱讀這本書[「高性能Java](http://shop.oreilly.com/product/9780596802806.do),他們說你確實需要將timeout設置爲25ms左右,否則就執行JavaScript會阻止重畫頁面,這可能是他提到的阻塞的原因。 – JustGage

4

不幸的是,JavaScript的setIntervalsetTimeout不是真正異步的,所以一切仍將在單線程中運行。這意味着當你的setInterval回調被調用時,它會在主線程中運行,並阻止其他任何事情的執行。

以下是John Resig撰寫的一篇很好的文章,詳細討論了這一點:How JavaScript Timers Work

如果你想真正的異步JavaScript,你必須使用像Web Workers


正如其他人在這裏指出,在您的特定情況下,你確實有在這條線的無限循環:

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

這是造成你的代碼:

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

要永遠叫做。有關更多信息,請參見Guffa's excellent answer

雖然我的答案的其餘部分仍然適用 - 如果您試圖做的不僅僅是簡單的操作,還會同時動畫一個.gif,但如果不使用網絡工作者之類的東西,您可能仍然會凍結或口吃。

+0

是的我知道,但我給setInterval 100毫秒暫停,這應該瀏覽器解凍,並在那些做其他事情100毫秒,不是?你可以評論這 – Hubro

+0

@Codemonkey:請參閱我的編輯。 Guffa的答案可能就是你想要的。 –

+0

收回我的downvote – Hubro

1

我注意到,在你的代碼setInterval其實是從來沒有所謂的,因爲在這一行

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

條件總是如此,因爲i總是比i+workload小,所以它永遠走不出的怪圈。好東西,你從循環內的函數返回。