說到 javascript 中的計時器,我們一定會想到 setTimeout() 和 setInterval() 這兩個函數。本文將從事件循環(Event Loop) 的角度來分析兩者的工作原理和差異。
setTimeout()
MDN對 setTimeout 的定義為:
在指定的延遲時間之後呼叫一個函數或執行一個程式碼片段。
語法
setTimeout 的語法非常簡單,第一個參數為回調函數,第二個參數為延時的時間。函數返回一個數值類型的ID唯一標示符,此ID可以用作clearTimeout 的參數來取消定時器:
var timeoutID = window.setTimeout(code, delay);
IE0+ 還支持回調參數的傳入:
var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);
setInterval()
〜MDN 對setInterval 的定義為一個週期或執行一段程式碼。
由於 setInterval 和 setTimeout 的用法一樣,所以這裡不再列出。
對第二個參數(delay)的說明
由於javascript 的事件循環機制,導致第二個參數並不代表延遲delay毫秒之後立即執行回呼函數,而是嘗試將回呼函數加入到事件隊列。實際上,setTimeout 和 setInterval 在這一點上處理又存在區別:
setTimeout:延時delay毫秒之後,啥也不管,直接將回呼函數加入事件隊列。
setInterval: 延時delay毫秒之後,先看看事件佇列中是否存在還沒執行的回呼函數(setInterval的回呼函數),如果存在,就不要再往事件佇列裡加入回呼函數了。
所以,當我們的程式碼中存在耗時的任務時,定時器並不會表現的如我們所想的那樣。
透過一個例子來理解
下面的程式碼,本來希望能夠在100ms 和200ms 的時候(也就是剛好等待100ms)呼叫回呼函數:
var timerStart1 = now(); console.log('第一個setTimeout回呼執行等待時間:', now() - timerStart1);
var timerStart2 = now();
setTimeout(function () {
調 cons cons ('第二個set
調 cons 執行等待時間:', now() - timerStart2);
}, 100);
}, 100);
// 輸出:
// 第一setTimeout回呼執行等待時間: 106
// 第二個setTimeout回呼執行等待時間: 107
這樣的結果看上去正是我們所想的那樣,但是一旦我們在程式碼中加入了耗時的任務時候,結果就不像我們所期望的那樣了:
setTimeout(function () {
console.log('第一個setTimeout回呼執行等待時間:', now() - timerStart1);
var timerStart2 = now(); () {
console.log('第二個setTimeout回呼執行等待時間:', now() - timerStart2);
}, 100);
heavyTask(); // 耗時任務
heavyTask(); // 耗時任務
);
var loopStart = now();
heavyTask(); // 耗時任務
console.log('heavyTask耗費時間:', now() - loopStart);
function heavyTask()
function heavyTask()
();
while(now() - s }
}
function now () {
return new Date();
}
// 輸出:150/Lask/Lask/ / 第一個setTimeout回呼執行等待時間: 1018
// 第二個setTimeout回呼執行等待時間: 1000
再看setInterval 的一個例子:
var intervalStart = now();
setInterval(function () {
console.log('intervalvalStartInterval(function () {
console.log('intervalvalStart);
}, 100);
var loopStart = now();
heavyTask();
console.log('heavyTask耗費時間:', now() - loopStart);
function now();
while(now() - s }
}
function now () {
return new Date();
}
// 輸出
// interval距定義定時器的時間: 1016
// interval距定義定時器的時間: 1123
// interval距定義定時器的時間: 1224