JS定時器,你所要了解的那點事

Gladyu發表於2018-04-16

背景:

開發過程中我們常常要用到定時器去完成某些需求。js為我們準備了兩個函式:setTimeout和setInterval,我們在進入主題之前,先簡單介紹一下它們

首先,它們都會返回一個整數編號用來表示定時器。將這個值傳給各自的clear方法可以取消定時。

其次,setTimeout是期望推遲(delay)ms後執行函式, setInterval則是期望間隔(delay)ms就執行一次函式。

為什麼只是說期望那?上一個?瞧瞧

先預測一下結果再執行程式碼,結果如下:

JS定時器,你所要了解的那點事

跟你想象中的結果一樣嗎?你會發現setTimeout並不是像它期望的那樣—-延遲1s就執行,而是要等待bulky()執行結束後才執行,這就是定時器不定時的情形。

定時器是怎麼實現定時的?為什麼會出現不定時的情況?

首先,要明確的一點:javascript是以單執行緒的方式執行的。JavaScript的主要用途是與使用者互動,以及操作DOM。若以多執行緒的方式,則可能出現衝突。假設有兩個執行緒同時操作一個DOM元素,執行緒1要求瀏覽器刪除DOM,而執行緒2卻要求修改DOM樣式,這時瀏覽器就無法決定採用哪個執行緒的操作。當然,我們可以為瀏覽器引入“鎖”的機制來解決這些衝突,但大大提高複雜性,所以 JavaScript從誕生開始就選擇了單執行緒執行。在某一時刻內只能執行特定的一個任務,並且會阻塞其它任務執行。

但是JavaScript 有個基於“Event Loop”併發的模型(不是並行)。前者是邏輯上的同時發生,而後者是物理上的同時發生。所以,單核處理器也能實現併發。

上圖說明一下併發和並行:

JS定時器,你所要了解的那點事

再上圖說明一下Event Loop

JS定時器,你所要了解的那點事

js既然是單執行緒的,也就意味著所有任務需要排隊。所有任務可以分成兩種,一種是同步任務(synchronous),另一種是非同步任務(asynchronous)

  • 同步任務指的是,在主執行緒上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務,形成了一個執行棧(execution context stack)
  • 非同步任務指的是,不進入主執行緒,而進入”任務佇列”(task queue)的任務。”任務佇列”是一個事件的佇列(可以當作訊息的佇列來理解)。IO裝置完成一項任務or非同步任務有了執行結果,就在”任務佇列”中新增一個事件,表示相關的操作可以進入”執行棧”,就等著執行棧呼叫了。

參照Event Loop的圖,說一下大致的流程—-> 執行棧中流式執行函式(同步任務),可能會呼叫API在任務佇列中加入事件(onlick,onload等等)但並不執行。只有當前執行棧沒有其他操作時,任務佇列才會入執行棧中,執行

原理講完了,回到正題。因為setTimeout, setInterval是非同步任務,呼叫之後不會直接進入執行棧,而是進入任務佇列,所以只有等到當前執行棧沒有其他操作,它們才會進入執行棧中執行, 以上就是為什麼定時器不總是定時的原因了。

哦,對了如果delay時間週期設為0,相當於一個插隊操作

舉例

?獻上:

執行結果

JS定時器,你所要了解的那點事

好了,以上內容就是我對js定時器的認識,希望可以幫助到你

未經本人允許,不得轉載。文章有疏漏淺薄之處,請各位大神斧正

相關文章