JavaScript 計時器

lwenn發表於2018-06-07

計時器

  • var id = setTimeout(fn, delay):開始一個timer,將在delay時間後呼叫fn
  • var id = setInterval(fn, delay)
  • clearInterval(id), clearTimeout(id)

計時器執行時間

計時器的延遲是沒有保證的。

由於瀏覽器中的 JS 都在單執行緒裡執行的,因此只有在執行過程中空閒時才會執行非同步事件(包括滑鼠點選和計時器)。

最直觀的例子:

setTimeout(function() {
  console.log(1);
}, 0);
console.log(2);
複製程式碼

輸出結果是 2 1。上例中看似在 0ms 後列印 1,但事實上是在執行完同步的 JS 後才執行計時器內的程式碼。

非同步佇列

非同步事件(包括計時器、滑鼠事件、XMLHttpRequest完成等)會被加入一個佇列等待執行,佇列的實現因瀏覽器而異。

image

在同步程式碼執行時,把所有非同步事件加入佇列中。當同步程式碼執行完後(18ms),先執行滑鼠點選事件(10ms),然後執行計時器,這時已經過了 28ms。

Intervals 不關心當前執行的是什麼,他們會無差別地新增到佇列中。圖中的 interval 註冊是 10ms 間隔,但是在執行過程中因為 interval 回撥函式本身的執行時間大於或等於 10ms,就會犧牲掉 interval 每個回撥函式之間的間隔。

setTimeout 和 setInterval

setTimeout(function(){
  // ...
  setTimeout(arguments.callee, 10);
}, 10);
 
setInterval(function(){
  // ...
}, 10);
複製程式碼

上面的兩種寫法看起來結果是一樣的,但是實際上有差別。由於 setTimeout 的寫法是每次在回撥函式執行時新增一個新的計時器,所以這個回撥每次執行的間隔至少是 10ms。而 setInterval 的寫法使回撥每 10ms 執行一次。

要點總結

  • JavaScript 單執行緒使得非同步事件要排隊等待執行。
  • setTimeoutsetInterval 本質上不同。
  • setInterval 註冊的間隔是回撥開始執行的間隔,如果回撥執行超過間隔時間,就會連續無延遲地執行。

參考

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

相關文章