1. 程式人生 > >JavaScript的定時器是如何工作的

JavaScript的定時器是如何工作的

理解JavaScript定時器工作原理對於學習JavaScript非常重要。因為JavaScript是單執行緒執行的,定時器使用場合少,不是很直觀。下面通過三個函式來學習JavaScript如何定義,操作及銷燬一個定時器。

  • var id = setTimeout(fn, delay); - 定義一個定時器,在指定時間delay後呼叫函式fn。函式返回一個唯一的標識ID,如果不需要使用這個定時器可以用這個取消。
  • var id = setInterval(fn, delay);- 類似setTimeout,但是會每隔指定時間delay呼叫指定函式fn,直至取消這個定時器。
  • clearTimeout(id);clearInterval(id); -這兩個函式接受一個標識ID(分別對應上面兩個函式返回的ID),停止定時器的回撥。

 要理解定時器如何工作,首先要弄清楚一個概念,定時器的回撥函式不能保證在指定時間delay一定執行。由於瀏覽器中的所有JavaScript都在單執行緒上執行,因此非同步事件(例如滑鼠單擊和計時器)僅在執行中存在同步時間執行完有空缺時才執行。參考下圖:

這張圖裡麵包含很多資訊,要想理解他們需要首先理解JavaScript非同步執行機制。這張圖中垂直方向上有時間刻度,以毫秒為單位,藍色部分表示正在執行的JavaScript事件。例如,第一塊JavaScript執行大約18毫秒,Mouse Click Callback大約執行11毫秒,後面以此類推。

由於JavaScript是單執行緒的,不能同時執行兩段JavaScript程式碼,所以上面的藍色的”塊“都是在上一個塊執行完才能執行下一個塊。這意味這當一個非同步任務(例如,點選滑鼠事件,定時器,ajax訪問)出現的時候,它將被放入到非同步佇列(放入佇列的方式和瀏覽器有關,不同瀏覽器有不同的實現)並隨後執行。

首先,在第一個程式碼塊中,JavaScript程式碼中首先出先兩個定時器:10ms Timer starts,10ms Interval starts。這兩個定時器的回撥函式何時執行取決於第一個程式碼塊的所有程式碼何時執行完。請注意,由於單執行緒的原因,它不會立即執行。

同時,在第一個程式碼塊裡,還有一個滑鼠事件Mouse Click Occurs,和上面的定時器一樣,這個非同步事件(點選滑鼠這種使用者互動是非同步執行的,因為JavaScript不知道使用者什麼時候會點選滑鼠)的回撥不會立即執行,而是放在非同步佇列裡排隊等待執行。

在初始的程式碼塊執行完畢之後,瀏覽器隨即開始輪詢這個非同步佇列:有那些操作等待著被執行呢?在這個例子中,滑鼠點選。瀏覽器會選擇一個立即執行(這裡是滑鼠點選的回撥時間)。定時器會等待指定的時間delay,然後執行。

注意點選滑鼠回撥事件在第一個事件迴圈,定時器回撥在隨後的迴圈中處理。但是,在後面的事件迴圈中(在執行定時器處理程式時),setTimeout定時器回撥函式就會被拋棄,不再執行了。如果在多個定時器之後有一大段同步任務執行,則同步任務執行完之後這些定時器回撥會被立即執行,沒有延遲(這個延遲可能很小,就是事件迴圈的間隔),直至完成。瀏覽器傾向於等到沒有更多的非同步任務被加入到非同步任務佇列中再開始執行。

實際上,我們可以看到在事件迴圈本身正在執行的同時觸發了第三個回撥的情況。 這向我們顯示了一個重要的事實:事件迴圈不關心當前正在執行的內容,它們會不加區分地排隊,即使這意味著從觸發事件,到滿足條件執行回撥函式之間,有一部分事件被浪費掉了。

最後,在第二個事件回撥完成執行之後,我們可以看到JavaScript引擎沒有執行的剩餘內容。 這意味著瀏覽器現在等待新的非同步事件發生。 當間隔再次觸發時,我們在50ms處獲得此值。 但是,這次沒有任何任務阻止它的執行,因此它自己立即觸發。

讓我們看一個示例,以更好地說明setTimeout和setInterval之間的差異。 

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

 

兩者的不同之處在於setTimeout在在10毫秒的delay之後執行程式碼(只會多餘10毫秒,絕不會少),setInterval則在每隔10毫秒的延遲時執行回撥程式碼,不管上次的回撥是否已經執行完。

好了,本文中介紹的一些要點,現在回顧一下:

  • JavaScript引擎在執行時只有一個執行緒,從而迫使非同步事件排隊等待執行。
  • setTimeout和setInterval在執行非同步程式碼的方式上不同。
  • 如果定時器被阻止立即執行,它將延遲到下一個可能的執行點(比所需的延遲時間更長)。
  • 如果定時器延遲的事件足夠長,則在到點後會立即執行,沒有延遲。

所有這些都是非常重要的基礎知識。 瞭解JavaScript引擎的工作原理,尤其是在通常發生大量非同步事件的情況下,為構建高階應用程式程式碼奠定了良好的基礎。

來源:https://johnresig.com/blog/how-javascript-timers-work/

本文是John Resig很早的一篇文章,通過setTimeout,setInterval兩個函式的比較,可以瞭解JavaScript事件迴圈機