代碼使用方法:
說明:這篇文章節選自John Resig 的《Secrets of the JavaScript Ninja》一書,本人翻譯只是供你們學習,翻譯不足之處,請斧正。
這篇文章主要從下面幾個方面解讀計時器:
計時器概述;
計時器速度深度探析;
用計時器處理大量任務;
利用計時器管理動畫;
較好的計時器測試
計 時器是一個咱們瞭解不多且常常被濫用的東西,它是javascript的特點。實際上,在複雜的應用程序開發中,它能爲咱們提供不少幫助。計時器提供了一 個能夠將代碼片斷異步延時執行的能力,javascript生來是單線程的(在必定時間範圍內僅一部分js代碼能運行),計時器爲咱們提供了一種避開這種 限制的方法,從而開闢了另外一條執行代碼的蹊徑。
有趣的是,與咱們廣泛接受的觀點相反,計時器並非javascript語言的一部分,而是瀏覽器引入的方法和對象的一部分。這意味着若是你選擇在一個非瀏覽器的環境運行它,頗有可能計時器不存在,你必須使用特定功能推行你本身的版本(如Rhino線程)。
一、計時器是如何工做的
從根本上來講,理解計時器如何工做很重要。一般狀況下,計時器的行爲並不直觀,由於它在一個單獨的線程中,讓咱們從三個函數的測試開始,對於每個函數咱們都有機會構建和控制計時器。
var id = setTimeout(fn,delay);啓動一個計時器,它將在延遲時間以後調用特定的函數,該函數返回一個惟一的ID,利用這個ID計時器在稍後的時間裏被取消;
var id = setInterval(fn,delay);與setTimeout類似,但它不斷的調用函數(每隔必定延遲時間)直到它被取消;
clearInterval(id),clearTimeout(id);接受計時器的 ID(由上述任意一個函數返回)並中止調用計時器。
爲了理解計時器內部是如何工做的,有一個很重要的概念須要加以探討:延遲是沒法保證的。既然瀏覽器中全部javascript 是在一個單線程中運行的,那麼異步事件(如鼠標點擊、計時器)在執行中也只有存在開放狀態時才運行,下面這張圖很好的說明了這個問題:
這張圖有不少信息須要消化,充分理解它將使你對異步js執行有一個更好的認識,圖表是一維的,在垂直方向上是時間(掛鐘),以毫秒爲單位。藍色盒子表明js執行的比例。例如,第一個javascript塊運行時間大約爲18秒,鼠標點擊大約爲11秒等等。
既 然javascript在必定時間內之執行一部分代碼(源於單線程的特性),那麼這些代碼塊的每個就被封鎖在其它異步事件執行的進程中。這代表當一個異 步事件發生時(如鼠標點擊、計時器釋放、XMLHttpRequest請求完成),它將排隊等候執行(如何排隊在不一樣瀏覽器之間是不同的)。
首 先,在第一代碼塊裏,有兩個計時器觸發:一個是10ms的setTimeout,一個是10ms的setInterval。在第一個代碼塊真正完成以前, 它實際上已經釋放了。可是,注意,它不會當即執行(因爲單線程的問題,它無能爲力),相反,爲了能在下一個可行的時間獲得執行,那些延時函數被編入隊列。
另外,在第一個代碼塊內,咱們看到鼠標點擊出現。與這個異步事件(咱們永遠不知道什麼時候執行動做,這樣就能夠認爲它是不一樣步的)相關Javascript回調函數跟初始的計時器同樣不能當即被執行,它排隊等候執行。
在Javascript最初的代碼塊執行完畢以後,瀏覽器會發出疑問:正在等候執行的是什麼?在這種狀況下,鼠標點擊處理器和計時器回調函數同時處於等待之中,而後,瀏覽器將選擇一個並當即執行它,計時器函數將等到下一個可能的時間執行。
注 意,當鼠標點擊函數處理器執行時,第一個回調函數也在執行,至於計時器,其處理器被編入隊列稍後執行。可是,請注意,當Interval再次釋放時(在計 時器處理器執行時),計時器執行的時間將減小。若是你在一大塊代碼執行期間將全部的Interval回調函數編入隊列,其結果是一大羣Interval回 調函數會毫無延遲的執行,直到所有完成。而瀏覽器在隊列增大以前只是簡單的等到沒有Interval處理器排隊爲止(間歇問題)。
事實上,咱們看到這樣一個狀況:Interval正在執行時,第三個Interval函數將釋放。這代表一個重要的事實:Interval對當前正在執行什麼不聞不問,它們將不會青紅皁白的排隊,即便是犧牲回調函數之間的時間也在所不辭。
最後,在第二個Interval函數執行完畢以後,咱們能夠看到沒有留下任何Javascript引擎執行的東西。也就是說,瀏覽器在等待一個新的異步事件的出現。Interval再次釋放時,咱們在50ms處得到它,但這一次,執行起來沒有任何障礙,它當即釋放。
咱們來看一個例子,以便更好的說明setTimeout和setInterval的差別:
Select All
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只有單線程,異步事件被迫排隊等候執行;
setTimeout和setInterval在如何執行異步代碼方面有根本的區別;
若是計時器沒法當即執行,它將延時到下一個可能的時間執行(這比預想的延遲時間要長一些);
若是有充分的執行時間,Interval可能會毫無延遲的來回執行。
全部這些無疑是重要的知識,瞭解Javascript引擎如何工做,特別是有大量異步事件出現時,這使得在構建高級應用代碼片斷時有一個良好的基礎。
二、計時器最小延時時間和可靠性
很明顯,你能夠延遲幾秒鐘、幾分鐘、幾小時或任何你你想要的時間間隔,但最不明顯的是你能選擇的最小延時時間。
在必定程度上,瀏覽器不能爲計時器提供良好的解決方案用以精確的處理它們(由於它們自身受操做系統時間的限制)。可是,縱觀全部的瀏覽器,能夠很安全的說,最小延時時間大約是10-15ms。
咱們能夠對跨平臺假定的計時器間歇做簡單的分析後得出這一結論。例如,若是咱們分析延遲時間爲0ms的setInterval,咱們會發如今大多數瀏覽器中的最小延遲時間。
在OS操做系統下的瀏覽器中:
從左上角開始,依次爲:Firefox 2, Safari 3, Firefox 3, Opera 9
在Windows操做系統下得瀏覽器中:
從左上角開始依次爲:Firefox 2, Internet Explorer 6, Firefox 3, Opera 9
setTimeout(function(){
/* Some long block of code... */
setTimeout(arguments.callee, 10);
}, 10);
setInterval(function(){
/* Some long block of code... */
}, 10);
上面圖表中的線條和數字顯示了瀏覽器同時處理時間間歇的數量,咱們能夠得出結論:在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有差別,你應該從新思考你應用程序代碼的結構)。