[ Javascript ] JavaScript中的定時器(Timer) 是如何工作的!

來源:互聯網
上載者:User

標籤:javascript   javascript引擎   瀏覽器   非同步   線程   

作為入門者來說,瞭解JavaScript中timer的工作方式是很重要的。通常它們的表現行為並不是那麼地直觀,而這是因為它們都處在一個單一線程中。讓我們先來看一看三個用來建立以及操作timer的函數。

var id = setTimeout(fn, delay); 

- 初始化一個單一的timer,這個timer將會在一定延時後去調用指定的函數。這個函數(setTimeout)將返回一個唯一的ID,我們可以通過這個ID來取消timer。

var id = setInterval(fn, delay);
- 與setTimeout類似,只不過它會持續地調用指定的函數(每次都有一個延時),直到timer被取消為止。

clearInterval(id);, clearTimeout(id);
- 接受一個timer的ID(由上述的兩個函數返回的),並且停止timer的回調事件。

要弄明白這個定時器內部是怎麼工作的,有一個很重要的概念需要被提出來:

1 定時器延遲是不準確的(guaranteed).因為所有的javascript 瀏覽器雄代碼只有一個單線程去執行,並且那些非同步事件(如滑鼠點擊事件,和定時器)只會當出現線程閒置時候去會執行。這有一個圖表的示範,如下:



這裡有很多資訊在這個圖中需要去理解,但是完全理解它之後,你會對javascript中非同步機制有一個清楚的認識。這個圖是一維的:

垂直方向我們用毫秒單位來標記這個時間,這個藍色的方塊代表這個正在被執行的javascript代碼。例如,這第一個被執行的javascript代碼大約花了18毫秒,這個滑鼠事件塊大約話費了11毫秒,等等。

由於javascript 引擎永遠只會執行一個片段的代碼在同一個時間(因為這個單線程機制),那麼這時每一個代碼塊將會阻塞(blocking)別的非同步事件的運行。

這意味著當一個非同步事件被調用(例如當一個滑鼠點擊,一個定時器觸發firing,或者一個xmlhttprequest 過程完成),它將會被加入到隊裡,並順延強制(至於具體如何被入到隊列中,不同的瀏覽器有不同的實現,我們這裡只考慮簡單的情況)

從一開始,在第一個javasript中,有兩個定時器被初始化了: 一個10 毫秒的 setTimeout時間和一個10 毫秒的setInterval事件(這裡注意,僅僅只是初始化,抑或叫作定義)。

由於這個定時器開始的時間和位置,導致它們在第一個javascript塊完成前就已經真正被調用(這裡的調用,並非直接執行,這裡需要注意,可以理解為只是準備調用,把該回調方法加入到隊列)了。注意,無論怎麼樣(however),定時器都不會立刻執行(因為線程沒有閒置原因,它沒辦法直接執行)。相反,這個被延遲的方法會被加入到隊列中,在某個可以執行的時刻(線程閒置時刻)執行。

另外一點,在第一個javascript塊中,我們可以看到還有一個滑鼠時間被觸發了。這個javascript 回調方法被關聯到一個非同步事件 (沒人知道使用者什麼時間做這個動作,所以它被認為是非同步),這個非同步事件也不會立刻執行,和上面的定時器一樣,也會被加入到隊列中。

在第一個javascript 塊執行結束之後,javascript 引擎就會立刻問一個問題: 還有什麼在等待被執行的代碼嗎? 那麼這個時間,有一個滑鼠事件回調和定時器回調同時在等待。這個瀏覽器馬上挑選一個(看,是滑鼠事件回調)立刻執行。這個定時器繼續等待,知道下一個可能的時刻。


注意一點:在這個這個滑鼠事件處理函數正在被執行的同時,第一個interval 回呼函數也會調用。正如前面提到的定時器一樣,它的回調方法會被加入到隊列中。然而,注意當這個interval再一次被調用(這個時候這個定時器的回調方法正在被執行),那麼這個時候,這個interval 的回調方法將會被刪除(drop)。 如果由於主線程需要執行很長時間的代碼塊,導致你在隊列中加入了很多個回調方法,那麼當這個主線程結束之後,一連串的回呼函數連續執行沒有間隔,直到結束。比較好的做法,是暫時讓瀏覽器休息等待一會,讓隊列中沒有Interval回調。

我們在看到一些情況:在第三個interval 回調方法觸發的時候,inteval自身正在執行(這裡應該是下在執行第二個interval沒有結束)。這裡給我們展示了一個重要的資訊:

interval 不會去關心當前的線程現在執行什麼,它們會把自己的回調方地加入到隊列中在任何情況下,即使它會讓兩個間隔的回調方法之間的時間減少。

最後,在第二個interval(圖中應該是第三個,這裡應為中間有一個被drop掉了)被 執行完之後,javasript引擎中已經沒有東西可以用來執行了。這也就是說,瀏覽器現在正在等一個新的非同步事件需要去觸發(occur)。在第50毫秒的時候,再一次觸發了inteval回調。這個時候,已經沒有東西去阻塞它的執行,所以它加入到隊列中之後就立刻執行了。

接下來,讓我們看一個例子更好的理解setTimeout與setInterval的區別:

setTimeout(function(){    /* Some long block of code... */    setTimeout(arguments.callee, 10);  }, 10);   setInterval(function(){    /* Some long block of code... */  }, 10);

這兩段代碼可能在功能的實現上非常的相似,不經意一看,他們是完全一樣的。尤其是這個setTimeout代碼會在上一個回呼函數執行之後至少隔10毫秒再執行一次回調方法(它可能會超過10毫秒,但不會少於10毫秒)。但是setInteval 卻會嘗試10毫秒就執行一個回呼函數,不會去管上一個回調是什麼時候執行的。

These two pieces of code may appear to be functionally equivalent, at first glance, but they are not. Notably the setTimeout code will always have at least a 10ms delay after the previous callback execution (it may end up being more, but never less) whereas the setInterval will attempt to execute a callback every 10ms regardless of when the last callback was executed.

這裡有一些東西是我們從這裡學到的,做一個總結:

1 javascript引擎僅僅只有一個單線程,正在執行的非同步事件會加入到隊列等待。

2 setTimeout與setInterval 是執行非同步回調方法從根本上不一樣的。

3 如果一個需要立即執行的定時器被阻塞了,它竟無法被順延強制,知道下一次線程空閑(那麼被延遲的時間讓會超過定時器定義的時間)

4 interval 可能會沒有延遲的連續執行回調方法,如果主線程了執行一個足夠長的代碼(比定時的延遲長)


所有的這些都是非常重要的知識對於瞭解javascript引擎是如何工作的。特別是對於大數量的回調事件發生的時候,為我們建立更好的應用代碼建立好的基礎。

----------------------------------------------------------------------------------------------------

原文出自jQuery的作者John Resig。

地址:http://ejohn.org/blog/how-javascript-timers-work/#postcomment



[ Javascript ] JavaScript中的定時器(Timer) 是如何工作的!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.