網頁制作Poluoluo文章簡介:計時器是一個我們了解很少且經常被濫用的東西,它是javascript的特色。實際上,在復雜的應用程序開發中,它能為我們提供很多幫助。計時器提供了一個可以將代碼片段異步延時執行的能力,javascript生來是單線程的(在一定時間范圍內僅一部分js代碼能運行),計時器為我們
說明:這篇文章節選自John Resig 的《Secrets of the JavaScript Ninja》一書,本人翻譯只是供大家學習,翻譯不足之處,請斧正。
這篇文章主要從下面幾個方面解讀計時器:
計時器是一個我們了解很少且經常被濫用的東西,它是javascript的特色。實際上,在復雜的應用程序開發中,它能為我們提供很多幫助。計時器提供了一個可以將代碼片段異步延時執行的能力,javascript生來是單線程的(在一定時間范圍內僅一部分js代碼能運行),計時器為我們提供了一種避開這種限制的方法,從而開辟了另一條執行代碼的蹊徑。
有趣的是,與我們普遍接受的觀點相反,計時器並不是javascript語言的一部分,而是浏覽器引入的方法和對象的一部分。這意味著如果你選擇在一個非浏覽器的環境運行它,很有可能計時器不存在,你必須使用特定功能推行你自己的版本(如Rhino線程)。
1、計時器是如何工作的
從根本上來說,理解計時器如何工作很重要。通常情況下,計時器的行為並不直觀,因為它在一個單獨的線程中,讓我們從三個函數的測試開始,對於每一個函數我們都有機會構建和控制計時器。
為了理解計時器內部是如何工作的,有一個很重要的概念需要加以探討:延遲是無法保證的。既然浏覽器中所有javascript 是在一個單線程中運行的,那麼異步事件(如鼠標點擊、計時器)在執行中也只有存在開放狀態時才運行,下面這張土很好的說明了這個問題:
這張圖有很多信息需要消化,充分理解它將使你對異步js執行有一個更好的認識,圖表是一維的,在垂直方向上是時間(掛鐘),以毫秒為單位。藍色盒子代表代表js執行的比例。例如,第一個javascript塊運行時間大約為18秒,鼠標點擊大約為11秒等等。
網頁制作Poluoluo文章簡介:計時器是一個我們了解很少且經常被濫用的東西,它是javascript的特色。實際上,在復雜的應用程序開發中,它能為我們提供很多幫助。計時器提供了一個可以將代碼片段異步延時執行的能力,javascript生來是單線程的(在一定時間范圍內僅一部分js代碼能運行),計時器為我們
既然javascript在一定時間內之執行一部分代碼(源於單線程的特性),那麼這些代碼塊的每一個就被封鎖在其它異步事件執行的進程中。這表明當一個異步事件發生時(如鼠標點擊、計時器釋放、XMLHttpRequest請求完成),它將排隊等候執行(如何排隊在不同浏覽器之間是不一樣的)。
首先,在第一代碼塊裡,有兩個計時器觸發:一個是10ms的setTimeout,一個是10ms的setInterval。在第一個代碼塊真正完成之前,它實際上已經釋放了。但是,注意,它不會立即執行(由於單線程的問題,它無能為力),相反,為了能在下一個可行的時間得到執行,那些延時函數被編入隊列。
另外,在第一個代碼塊內,我們看到鼠標點擊出現。與這個異步事件(我們永遠不知道何時執行動作,這樣就可以認為它是不同步的)相關Javascript回調函數跟初始的計時器一樣不能立即被執行,它排隊等候執行。
在Javascript最初的代碼塊執行完畢之後,浏覽器會發出疑問:正在等候執行的是什麼?在這種情況下,鼠標點擊處理器和計時器回調函數同時處於等待之中,然後,浏覽器將選擇一個並立即執行它,計時器函數將等到下一個可能的時間執行。
注意,當鼠標點擊函數處理器執行時,第一個回調函數也在執行,至於計時器,其處理器被編入隊列稍後執行。但是,請注意,當Interval再次釋放時(在計時器處理器執行時),計時器執行的時間將減少。如果你在一大塊代碼執行期間將所有的Interval回調函數編入隊列,其結果是一大群Interval回調函數會毫無延遲的執行,直到全部完成。而浏覽器在隊列增大之前只是簡單的等到沒有Interval處理器排隊為止(間歇問題)。
事實上,我們看到這樣一個情況:Interval正在執行時,第三個Interval函數將釋放。這表明一個重要的事實:Interval對當前正在執行什麼漠不關心,它們將不會青紅皂白的排隊,即使是犧牲回調函數之間的時間也在所不辭。
最後,在第二個Interval函數執行完畢之後,我們可以看到沒有留下任何Javascript引擎執行的東西。也就是說,浏覽器在等待一個新的異步事件的出現。Interval再次釋放時,我們在50ms處獲得它,但這一次,執行起來沒有任何障礙,它立即釋放。
我們來看一個例子,以便更好的說明setTimeout和setInterval的差異:
setTimeout(function(){
/* Some long block of code... */
setTimeout(arguments.callee, 10);
}, 10);
setInterval(function(){
/* Some long block of code... */
}, 10);
乍一看,這兩段代碼似乎功能相同,但並非如此。setTimeout代碼在前一個回調函數執行萬之後,至少有10ms的延遲(最終可能多些,但至少不會少於此),而setInterval將每隔10ms嘗試執行一次回調函數而不管最後一個回調函數何時執行。
這裡有很多我們需要了解,讓我們回顧一下:
所有這些無疑是重要的知識,了解Javascript引擎如何工作,特別是有大量異步事件出現時,這使得在構建高級應用代碼片段時有一個良好的基礎。
網頁制作Poluoluo文章簡介:計時器是一個我們了解很少且經常被濫用的東西,它是javascript的特色。實際上,在復雜的應用程序開發中,它能為我們提供很多幫助。計時器提供了一個可以將代碼片段異步延時執行的能力,javascript生來是單線程的(在一定時間范圍內僅一部分js代碼能運行),計時器為我們
2、計時器最小延時時間和可靠性
很明顯,你可以延遲幾秒鐘、幾分鐘、幾小時或任何你你想要的時間間隔,但最不明顯的是你能選擇的最小延時時間。
在一定程度上,浏覽器不能為計時器提供良好的解決方案用以精確的處理它們(因為它們自身受操作系統時間的限制)。但是,縱觀所有的浏覽器,可以很安全的說,最小延時時間大約是10-15ms。
我們可以對跨平台假定的計時器間歇作簡單的分析後得出這一結論。例如,如果我們分析延遲時間為0ms的setInterval,我們會發現在大多數浏覽器中的最小延遲時間。
在OS操作系統下的浏覽器中:
從左上角開始,依次為:Firefox 2, Safari 3, Firefox 3, Opera 9
在Windows操作系統下得浏覽器中:
從左上角開始一次為:Firefox 2, Internet Explorer 6, Firefox 3, Opera 9
上面圖表中的線條和數字顯示了浏覽器同時處理時間間歇的數量,我們可以得出結論:在OS上,浏覽器的最小延時時間為10ms,在windows上為15ms。我們可以通過為計時器提供0(或任何10ms以下的任何數值)作為延時時間得到這個值。
但有一個例外,IE為setInterval提供德爾延時時間不能為0(即使setTimeou能欣然的接受)。當setInterval的延時時間為0時,它會轉變成setTimeout(僅執行一次回調函數),而我們可以通過為其提供1ms的延遲來解決這個問題。由於所有浏覽器都能自動向上捨入任何低於最小延時時間的值,所以用1ms與有效的使用0ms一樣安全,或更安全(既然IE浏覽器現在能工作)。
從這些表中我們可以得到其它信息。最重要的是加強了我們以前所了解到的:浏覽器不能保證你所指定的精確的時間間歇。像Firefox 2,Opera 9(OS)在提供可靠的執行率方面有一定的難度。很多與浏覽器如何處理Javascript的垃圾回收有關(Firefox 3在Javascript的執行上作了顯著的改善,其垃圾回收在這些結果中立竿見影)。
因此,浏覽器可以提供非常小的延遲時間,但其精確度得不到保證,那麼在使用計時器時,你需要考慮你的應用程序(如果10ms和15ms有差異,你應該重新思考你應用程序代碼的結構)。
未完待續