計時器
var id = setTimeout(fn, delay)
:開始一個timer,將在delay時間後呼叫fnvar id = setInterval(fn, delay)
clearInterval(id)
,clearTimeout(id)
計時器執行時間
計時器的延遲是沒有保證的。
由於瀏覽器中的 JS 都在單執行緒裡執行的,因此只有在執行過程中空閒時才會執行非同步事件(包括滑鼠點選和計時器)。
最直觀的例子:
setTimeout(function() {
console.log(1);
}, 0);
console.log(2);
複製程式碼
輸出結果是 2 1。上例中看似在 0ms 後列印 1,但事實上是在執行完同步的 JS 後才執行計時器內的程式碼。
非同步佇列
非同步事件(包括計時器、滑鼠事件、XMLHttpRequest完成等)會被加入一個佇列等待執行,佇列的實現因瀏覽器而異。
在同步程式碼執行時,把所有非同步事件加入佇列中。當同步程式碼執行完後(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 單執行緒使得非同步事件要排隊等待執行。
setTimeout
和setInterval
本質上不同。setInterval
註冊的間隔是回撥開始執行的間隔,如果回撥執行超過間隔時間,就會連續無延遲地執行。
參考
https://johnresig.com/blog/how-javascript-timers-work/