最近都在看一些JavaScript原理層面的文章,恰巧看到了jQuery的做者的一篇關於JavaScript計時器原理的解析,因而坐臥不安地決定把原文翻譯成中文,一來是爲了和你們分享,二來是爲了加深本身對於JavaScript的理解。原文連接:http://ejohn.org/blog/how-javascript-timers-work/ javascript
從基礎層面來說,理解JavaScript計時器的工做原理是很重要的。因爲JavaScript是單線程的,因此不少時候計時器並非表現得和咱們的直觀想象同樣。讓咱們從下面的三個函數開始,它們可以讓咱們有機會去構造和操做計時器。 java
爲了理解計時器的內部工做原理,咱們首先須要瞭解一個很是重要的概念:計時器設定的延時是沒有保證的。由於全部在瀏覽器中執行的JavaScript單線程異步事件(好比鼠標點擊事件和計時器)都只有在它有空的時候才執行。這最好經過圖片來講明,就以下面這張圖所示: 瀏覽器
這一張圖片裏面有不少信息須要慢慢消化,可是完全地理解這張圖片將會讓你對JavaScript異步執行是如何工做的有一個更好的認識。這張圖片是從一維的角度來闡述的:在垂直方向是以毫秒計的時間,藍色的塊表明了 異步
當前正在執行的JavaScript代碼段。好比第一段JavaScript執行了大概18毫秒,鼠標點擊事件大概執行了11毫秒。 函數
因爲JavaScript每次只能執行一段代碼(基於它單線程的特性),因此全部這些代碼段都阻塞了其餘異步事件的執行。這就意味着,當一件異步事件(好比鼠標點擊,計時器觸發和一個XMLHttpRequest 請求完成)觸發的時候,這些事件的回調函數將排在執行隊列的最後去等待執行(排隊的方式因瀏覽器不一樣而不一樣,這裏只是一個簡化)。 spa
一開始,在第一段代碼段內,兩個計時器被初始化:一個10ms的 setTimeout 和一個10ms的setInterval。因爲計時器在哪兒初始化就在那兒開始計時,因此實際上計時器在第一段代碼執行完成以前就觸發了。然而,計時器的回調函數並非當即執行了(單線程限制了不能這樣作),相反的是,回調函數排在了執行隊列的最後,等到下一個有空的時間去執行。 線程
此外,在第一個代碼塊內咱們看到了一個鼠標點擊事件發生了。與之相關的javascript異步事件(咱們不可能預測用戶會在何時去採起這樣的動做,所以這個事件被視爲異步的)並不會當即執行。和計時器同樣的是,它被放到了隊列的最後去等待執行。 翻譯
在第一個代碼快執行完成的時候,瀏覽器會當即發出這樣的詢問:誰正在等待執行?這個時候,鼠標點擊處理程序和計時器回調函數都在等待執行。瀏覽器選擇了其中一個(鼠標點擊回調函數)而且當即執行它。爲了執行,計時器會等到下一個可能執行的時間。 code
咱們注意到,當鼠標點擊事件對應的處理程序正在執行的時候,第一個定時回調函數也要執行了。同定時計時器同樣,它也在隊列的後面等待執行。然而,咱們能夠注意到,當定時器再一次觸發(在計時器回調函數正在執行的時候),這一次定時器回調函數被丟棄了。若是在執行一大塊代碼塊的時候,你把全部的定時回調函數都放在隊列的最後,結果就是一大串定時回調函數將會沒有間隔的一塊兒執行,直到完成。相反,在把更多定時回調函數放到隊列以前,瀏覽器會靜靜的等待,知道隊列中的全部定時回調函數都執行完成。 blog
事實上,咱們能夠看到,當interval回調函數正在執行的時候,interval第三次被觸發。這給咱們一個很重要的信息:interval並不關心當前誰在執行,它的回調函數會不加區分地進入隊列,即便存在這個回調函數會被丟棄的可能。
最後,當第二個定時回調函數完成執行的時候,咱們能夠看到javascript引擎已經沒有什麼須要執行了。這意味着,瀏覽器如今正在等待一個新的異步事件的發生。咱們能夠看到在50ms的時候,定時回調函數再一次被觸發。然而,這一次,沒有其餘代碼阻塞他的執行了,因此他當即執行了定時回調函數。
讓咱們看一個例子來更好地闡述 setTimeout 和setInterval的區別。
1 setTimeout(function(){ 2 /* Some long block of code... */ 3 setTimeout(arguments.callee, 10); 4 }, 10); 5 6 setInterval(function(){ 7 /* Some long block of code... */ 8 }, 10);
第一眼看上去這兩段代碼在功能上是等價的,但事實上卻不是。值得注意的是,setTimeout 這段代碼會在每次回調函數執行以後至少須要延時10ms再去執行一次(多是更多,可是不會少)。可是setInterval會每隔10ms就去嘗試執行一次回調函數,無論上一個回調函數是否是還在執行。
從這裏咱們可以學到不少,讓咱們來歸納一下:
上述這一切對於理解js引擎是若是工做的無疑是很重要的知識,尤爲是大量的典型的異步事件發生時,對於構建一個高效的應用代碼片斷來講是一個很是有利的基礎。