在對JS本質理解的層面上,重要的是了解JS定時器是如何工作的。很多時候我們覺得定時器的執行不夠直觀,那因為它們運行在單線程的環境里。我們先來仔細觀察下面三個js的內置函數,然后我們再具體去使用它們:

上面這張圖片有很多信息可以挖掘,要是能夠完全的理解它,那將讓你更好的去領悟javascript異步工作是如何實現的。這是一張一維的圖片:從上到下 標注了執行的時間(鍾表),單位是毫秒(ms),每個方框表示javascript被執行的一部分。例如第一個方框的javascript大約執行了18ms,而鼠標點擊事件被阻塞了大約11ms等等。
由於在同一時間內只能執行一個javascript代碼塊(由於它的單線程特性),每一個這樣的代碼塊都會“阻塞”其他異步事件的執行。這意味着當一個異步事件出現時(例如鼠標點擊、定時器觸發、一個XMLHttpRequest請求完成),它會排隊等待被執行(由瀏覽器到瀏覽器如何確定這個隊列真正發生了呢,所以考慮到這些時,我們將會簡化說明).
以在第一個javascript代碼塊內作為開始,有兩個定時器被啟動:一個10ms的setTimeout和一個10ms的setInterval;由於何時何地定時器被啟動,它實際觸發在我們實際完成第一個代碼塊之前。注意,然而它並沒有立即被執行(他不能執行是因為單線程的原因),取而代之延遲函數(非fn,是delay的fn)被放進隊列里面,只是為了等待可用的時間片段然后再執行。
另外,在第一個javascript代碼方框內,我們看到鼠標點擊事件出現了,鼠標對應的回調函數與這個異步事件(我們從未得知何時用戶完成一個操作,因此鼠標點擊事件它認為這是異步的)相聯系,但是這個回調函數並不能立即被執行,就比如啟動了一個定時器,它會排隊等待被執行。
當一個javascript代碼塊執行完成之后,瀏覽器會去問一個問題:是什么代碼塊在等待被執行?在這樣的情況下,鼠標點擊事件處理函數和觸發器回調函數兩者都等待被執行,瀏覽器然后會選擇一個代碼塊(鼠標點擊處理函數)並且立即執行它,而定時器為了執行回調函數會繼續的在等待下去,遲到出現可利用的時間片刻。
注意,當鼠標點擊事件處理程序正常執行時,第一個間接回調函數(是setInterval中的delay對應的Interval回調函數)也執行,而它對應的處理程序和觸發器仍在等待以后被執行。然而,值得注意的是,當這個間接回調函數再次觸發時(當觸發器處理程序正在執行時),此時另一個處理程序的執行將被放棄。當一個大的代碼塊正在執行時,如果你是去排隊所有的Interval回調函數,那么結果將會是一堆Interva回調函數被執行,他們之間不會存在延遲,在完成之前。然而,瀏覽器們總是趨向於一味的等待沒有更多的Interval回調函數被排隊(之前有很多Interval回調函數在排隊)。事實上,我們可以看到第三個Interval回調函數觸發時,它自身正常執行。這些給我們說明了一個事實:Interval不關心什么是當前正在執行的,他們將進入到隊列里面去,即使那意味着將會犧牲掉回調處理函數之間的時間間隔。
最后,在第二個 Interval回調函數被執行完成之后,我可以看到沒有什么需要javascript引擎去執行。這意味着瀏覽器正在等待一個新的異步事件出現。在50ms標記的地方,當interval再次觸發時我們可以看到這樣的一個事實,然而這次沒有任務代碼塊阻塞其執行,所以它立即觸發。
為了更好的舉例說明setTimeout和setInterval的區別,我們來看看一個例子:
setTimeout(function(){ /* Some long block of code... */ setTimeout(arguments.callee, 10); }, 10); setInterval(function(){ /* Some long block of code... */ }, 10);
咋一看,這個兩個代碼塊看起來功能是相同的,其實他們並不一樣,尤其是setTimeout在前一個回調執行完之后,總是延遲至少10ms(可能更多,但從不了不會減少),然而setInterval每十秒都會企圖嘗試執行回調函數,不管何時最后一個回調被執行完。
上面我們在這里學習了很多內容,現在讓我一起來總結一下:
1.javascript引擎只有一個線程,強迫異步事件排隊等待被執行。
2.setTimeout和setInterval本質上不同的地方是他們如何執行異步代碼的。
3.如果一個定時器正在執行的時候被阻塞了,那么它將會被推遲到下一個可能的執行點,這既是使得延遲時間有可能會超過聲明定時器時設置的值。
4.Interval如果有足夠的時間來執行(大於制定的延遲),那么它將會無延遲的一個緊接着一個執行。
在通常情況下,對於構建一個高級的大規模javascript工程時,將會出現大量的異步事件,如何處理好這些問題就得依靠積累和深入體會上面這些知識,知道javascript引擎是如何工作的,這是很重要也很難以置信的。