JavaScript定時器的工作原理(翻譯)
标簽(空格分隔): JavaScript定時器
最近在看ajax原理的時候,看到了一篇國外的文章,講解了JavaScript定時器的工作原理,幫助我很好的了解了js的單線程工作模式。在這裡翻譯一下供大家參考,
原文位址.
翻譯正文
從根本上講,了解JavaScript定時器的工作原理非常重要。通常 js的單線程表現的。讓我們首先看一下可以構造和操作定時器的三個函數。
- 啟動單個定時器,在延遲後調用指定的功能。該函數傳回一個唯一的ID,該Id可以用于取消定時器
var id = setTimeout(fn, delay);
- 類似setTimeout但不斷地調用函數(每次都有延遲),直到它被取消,類似于定時任務。同上也傳回唯一ID用于取消定時器
var id = setInterval(fn, delay);
- 接受計時器ID(由上述任一功能傳回)并停止觸發計時器回調。
clearInterval(id);
clearTimeout(id);
為了了解定時器的内部工作原理,我們需要探索一個重要的概念:定時器延遲并不能保證準确。由于浏覽器中的所有JavaScript都在單個線程上執行,是以異步事件(例如滑鼠單擊和計時器)僅在可被執行時運作。使用圖表可以最好地證明這一點,如下所示:
這個圖中有很多資訊需要了解,完全了解它會讓你更好地了解異步JavaScript執行的工作方式。這個圖是一維的:垂直方向,我們有(挂鐘)時間(譯注:左側的時間刻度),以毫秒為機關。藍色框表示正在執行的JavaScript部分。例如,第一個JavaScript塊執行大約18ms,滑鼠單擊塊大約需要11ms,依此類推。
由于JavaScript一次隻能執行一段代碼(由其單線程性質決定),是以每個代碼塊都“阻塞”其他異步事件的進度。這意味着當異步事件發生時(如滑鼠單擊,計時器觸發或XMLHttpRequest完成),它會先排隊等待(譯注:可以了解為一個隊列)以後執行(這種排隊實際發生的方式因浏覽器到浏覽器而異,這裡是做了一個簡單說明)。
首先,在JavaScript的第一個塊中,啟動兩個定時器:10ms setTimeout和10ms setInterval。由于計時器啟動的位置和時間,它實際上在我們實際完成第一個代碼塊之前觸發。但請注意,它不會立即執行(由于線程,它無法執行此操作)而是進入排隊隊列,以便在下一個可用時刻執行。
此外,在第一個JavaScript塊中,我們看到滑鼠單擊。滑鼠點選事件關聯的JavaScript回調(我們永遠不知道使用者何時執行一個操作,是以它被認為是異步的)無法立即執行,是以,與初始計時器一樣,它排隊等待稍後執行。
在JavaScript的初始塊完成後,執行浏覽器會立即詢問:隊列中有什麼等待執行的任務?在這種情況下,滑鼠單擊處理程式和計時器回調都在等待。然後浏覽器選擇一個(滑鼠單擊回調)并立即執行它。計時器将等到下一個時間被從隊列中取出執行。
請注意,當滑鼠單擊處理程式執行時,執行第一個間隔回調。與計時器一樣,它的處理程式進入排隊等待以後執行。但是,請注意,當再次觸發間隔時(在執行timer程式時),此時Interval的間隔回調被丢棄(譯者注:這裡不是很了解,歡迎留言交流指教。是否是因為已經有interval在排隊?)。如果要在執行大塊代碼時調用間隔回調,間隔回調将連續加入到任務隊列中,它們之間沒有延遲。浏覽器往往隻是簡單的去隊列中取任務直到隊列中沒有其他任務。
事實上,我們可以看到,當間隔本身正在執行時,第三個間隔回調會觸發。這向我們展示了一個重要的事實:Intervals不關心目前正在執行的内容,它們會不加選擇地排隊。
最後,在第二個間隔回調完成執行後,我們可以看到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代碼在前一次回調執行後總是至少有10ms的延遲(它可能最終會更多,但絕不會少于10ms),而setInterval在這段長代碼執行期間它都會嘗試每10ms執行一次回調(譯者注:可了解為在這段長代碼執行期間,會每10ms添加一個任務進隊列,中間無間隔)。
我們在這裡學到了很多,讓我們回顧一下:
JavaScript引擎隻有一個線程,迫使異步事件排隊等待執行。
setTimeout并且setInterval它們在執行異步代碼方面有着根本的不同。
如果計時器被阻止立即執行,它将被延遲到下一個可能的執行點(這将超過所需的延遲)。
如果setInterval中回調程式執行時間足夠長(超過指定的延遲),則間隔可以無延遲地執行(譯者注:因為在一個回調沒有運作完的時候就會再加入一個回調)。
所有這些都是用來了解JavaScript引擎的工作原理的非常重要的知識,尤其是發生的大量異步事件的時候,為建構進階應用程式代碼奠定了基礎。
希望大家多評論交流,互相學習