JavaScript中timer是如何工做的


    做爲入門者來講,瞭解JavaScript中timer的工做方式是很重要的。一般它們的表現行爲並非那麼地直觀,這是由於它們都處在單線程中。讓咱們先來看看三個用來出建立和操做timer的函數。javascript

var id = setTimeout(fn, delay);

    初始化這個timer,而後這個timer將會在delay延時後調用這個函數fn。這個函數將返回一個惟一的ID,能夠經過這個ID來取消timer。java


var id = setInterval(fn, delay);

    與setTimeout相似,不過它會持續調用函數fn(每隔delay毫秒),直到timer被取消。
瀏覽器

    

clearTimeout(id);
clertInterval(id);

    接受一個timer的ID,並中止timer的回調事件。
異步


    爲了理解timer內部工做原理,咱們還須要知道一個重要的概念:timer的延時是不許確的。因爲瀏覽器中的JavaScript在單線程中運行,所以異步事件(如mouse click以及timer)只有在執行期空閒的時候才運行。這個用圖表最能解釋清楚了,參看下圖:
函數

                             

    這個圖標中有大量的信息能夠挖掘,可是徹底理解它將使咱們對JavaScript中異步事件執行原理有個更深的認識。這是一張一維圖:數值方向爲時間,單位毫秒。藍色的框表示正在執行的JavaScript片斷。舉個例子,第一塊JavaScript大約執行了18ms,鼠標點擊則執行了11ms,以此類推。
spa

    因爲JavaScript在同一時間只能執行一段代碼(由單線程的本質決定),所以每一個代碼塊都會「阻塞」其餘異步事件。這意味着當異步事件發生時(好比鼠標點擊、timer觸發或者XMLHttpRequest完成),將進入事件隊列等待執行(隊列的實現方式因瀏覽器而異,這裏只討論簡化的狀況)。
線程

    從第一段JavaScript代碼塊開始,有兩個timer被初始化了:一個10ms的setTimeout和一個10ms的setInterval。其實在第一段代碼塊執行完以前timer就已經觸發了。注意,不管如何它都不會當即執行(因爲單線程緣由,它沒法那樣作)。相反,這個延時執行的函數進入隊列等待下次空閒的時候執行。
code

    此外,第一段代碼塊中鼠標點擊事件也發生了。所以和異步事件相關聯的回調函數也不能當即執行,像最初的timer同樣,也要進入隊列等待執行。
blog

    最初的代碼塊執行完之後,瀏覽器問了這樣一個問題:誰在等待執行?這個例子中,鼠標點擊事件和timer回調函數都處於等待中。瀏覽器挑了鼠標點擊事件,而後當即執行它。而timer將繼續等待下次執行。
隊列

    注意當鼠標點擊事件執行過程當中時,第一次的interval的回調也觸發了。和timer同樣,它的事件也將進入隊列等待執行。而後,當interval再次觸發的時候(此時timer正在執行),此次它的事件將被丟棄。若是你在一大塊代碼執行時,把全部的interval的回調事件排入隊列,其結果是在上述代碼執行完之後,一推的interval事件將毫無間隔地執行。而瀏覽器更趨向於等待,以確保interval事件進入隊列時沒有其餘的interval事件。

    事實上,從這個例子中能夠看出:當第三個interval觸發的時候,interval自身正在執行。這告訴咱們一個重要的事實:interval無論當前執行的是什麼,都會進入隊列,即便這樣意味着回調函數之間的時間就不許確了。

    最後,當第二個interval事件執行完之後,將沒有任何代碼留給JavaScript引擎執行。這意味着如今瀏覽器將等待新的異步事件觸發。因而在50ms的時候,interval再一次觸發。這一次,沒有什麼阻塞它的執行,它就當即觸發了。

    讓咱們看一個例子來更好的闡述setTimeout和setInterval之間的區別。

    

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

setInterval(function(){
    /*Some long block of code */
}, 10)

    咋看一下,這兩段代碼的功能彷佛是同樣的,但實際上並不是如此。尤爲是setTimeout這段代碼,在前一個回調函數執行完之後,都至少會有10ms的延時(可能會多,但毫不會少)。然而,setInterval的代碼則老是每10ms的時候嘗試執行回調,不管上一次回調何時執行的。

    重述下學到的知識。

  •  JavaScript引擎只有一個線程,這使得異步事件必須排入隊列等待執行

  •  setTimeout和setInterval在執行異步代碼上有着本質的區別

  •  若是timer在將要執行的時候被阻塞,它將等待下一個時機

  •  若是interval執行的時候很長(比指定的時間長),那麼它們將連續地執行而沒有延時。

    


原文:jQuery之父John Resig的Blog 

http://ejohn.org/blog/how-javascript-timers-work/

相關文章
相關標籤/搜索