說到 javascript 中的定時(shí)器,我們肯定會(huì)想到 setTimeout() 和 setInterval() 這兩個(gè)函數(shù)。本文將從事件循環(huán)(Event Loop) 的角度來分析兩者的工作原理和區(qū)別。
**setTimeout()**
MDN對 setTimeout 的定義為:
在指定的延遲時(shí)間之后調(diào)用一個(gè)函數(shù)或執(zhí)行一個(gè)代碼片段。
語法
setTimeout 的語法非常簡單,第一個(gè)參數(shù)為回調(diào)函數(shù),第二個(gè)參數(shù)為延時(shí)的時(shí)間。函數(shù)返回一個(gè)數(shù)值類型的ID唯一標(biāo)示符,此ID可以用作 clearTimeout 的參數(shù)來取消定時(shí)器:
var timeoutID = window.setTimeout(code, delay);
IE0+ 還支持回調(diào)參數(shù)的傳入:
var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);
setInterval()
MDN 對 setInterval 的定義為:
周期性地調(diào)用一個(gè)函數(shù)(function)或者執(zhí)行一段代碼。
由于 setInterval 和 setTimeout 的用法一樣,所以這里不再列出。
對第二個(gè)參數(shù)(delay)的說明
由于javascript 的事件循環(huán)機(jī)制,導(dǎo)致第二個(gè)參數(shù)并不代表延遲delay毫秒之后立即執(zhí)行回調(diào)函數(shù),而是嘗試將回調(diào)函數(shù)加入到事件隊(duì)列。實(shí)際上,setTimeout 和 setInterval 在這一點(diǎn)上處理又存在區(qū)別:
- setTimeout:延時(shí)delay毫秒之后,啥也不管,直接將回調(diào)函數(shù)加入事件隊(duì)列。
- setInterval: 延時(shí)delay毫秒之后,先看看事件隊(duì)列中是否存在還沒有執(zhí)行的回調(diào)函數(shù)(setInterval的回調(diào)函數(shù)),如果存在,就不要再往事件隊(duì)列里加入回調(diào)函數(shù)了。
所以,當(dāng)我們的代碼中存在耗時(shí)的任務(wù)時(shí),定時(shí)器并不會(huì)表現(xiàn)的如我們所想的那樣。
通過一個(gè)例子來理解
下面的代碼,本來希望能夠在 100ms 和 200ms 的時(shí)候(也就是剛好等待 100ms)調(diào)用回調(diào)函數(shù):
var timerStart1 = now();
setTimeout(function () {
console.log('第一個(gè)setTimeout回調(diào)執(zhí)行等待時(shí)間:', now() - timerStart1);
var timerStart2 = now();
setTimeout(function () {
console.log('第二個(gè)setTimeout回調(diào)執(zhí)行等待時(shí)間:', now() - timerStart2);
}, 100);
}, 100);
// 輸出:
// 第一個(gè)setTimeout回調(diào)執(zhí)行等待時(shí)間: 106
// 第二個(gè)setTimeout回調(diào)執(zhí)行等待時(shí)間: 107
這樣的結(jié)果看上去正是我們所想的那樣,但是一旦我們在代碼中加入了耗時(shí)的任務(wù)時(shí)候,結(jié)果就不像我們所期望的那樣了:
var timerStart1 = now();
setTimeout(function () {
console.log('第一個(gè)setTimeout回調(diào)執(zhí)行等待時(shí)間:', now() - timerStart1);
var timerStart2 = now();
setTimeout(function () {
console.log('第二個(gè)setTimeout回調(diào)執(zhí)行等待時(shí)間:', now() - timerStart2);
}, 100);
heavyTask(); // 耗時(shí)任務(wù)
}, 100);
var loopStart = now();
heavyTask(); // 耗時(shí)任務(wù)
console.log('heavyTask耗費(fèi)時(shí)間:', now() - loopStart);
function heavyTask() {
var s = now();
while(now() - s < 1000) {
}
}
function now () {
return new Date();
}
// 輸出:
// heavyTask耗費(fèi)時(shí)間: 1015
// 第一個(gè)setTimeout回調(diào)執(zhí)行等待時(shí)間: 1018
// 第二個(gè)setTimeout回調(diào)執(zhí)行等待時(shí)間: 1000
兩個(gè) setTimeout 的等待事件由于耗時(shí)任務(wù)的存在不再是 100ms 了!我們來描述一下事情的經(jīng)過:
- 首先,第一個(gè)耗時(shí)任務(wù)(heavyTask())開始執(zhí)行,它需要大約 1000ms 才能執(zhí)行完畢。
- 從耗時(shí)任務(wù)開始執(zhí)行,過了 100ms, 第一個(gè) setTimeout 的回調(diào)函數(shù)期望執(zhí)行,于是被加入到事件隊(duì)列,但是此時(shí)前面的耗時(shí)任務(wù)還沒執(zhí)行完,所以它只能在隊(duì)列中等待,直到耗時(shí)任務(wù)執(zhí)行完畢它才開始執(zhí)行,所以結(jié)果中我們開的看到的是: 第一個(gè)setTimeout回調(diào)執(zhí)行等待時(shí)間:1018。
- 第一個(gè) setTimeout 回調(diào)一執(zhí)行,又開啟了第二個(gè) setTimeout, 這個(gè)定時(shí)器也是期望延時(shí) 100ms 之后能夠執(zhí)行它的回調(diào)函數(shù)。但是,在第一個(gè) setTimeout 又存在一個(gè)耗時(shí)任務(wù),所有它的劇情跟第一個(gè)定時(shí)器一樣,也等待了 1000ms 才開始執(zhí)行。
可以用下面的圖來概括:
再來看 setInterval 的一個(gè)例子:
var intervalStart = now();
setInterval(function () {
console.log('interval距定義定時(shí)器的時(shí)間:', now() - loopStart);
}, 100);
var loopStart = now();
heavyTask();
console.log('heavyTask耗費(fèi)時(shí)間:', now() - loopStart);
function heavyTask() {
var s = now();
while(now() - s < 1000) {
}
}
function now () {
return new Date();
}
// 輸出:
// heavyTask耗費(fèi)時(shí)間: 1013
// interval距定義定時(shí)器的時(shí)間: 1016
// interval距定義定時(shí)器的時(shí)間: 1123
// interval距定義定時(shí)器的時(shí)間: 1224
上面這段代碼,我們期望每隔 100ms 就打出一條日志。相對于 setTimeout 的區(qū)別, setInterval 在準(zhǔn)備把回調(diào)函數(shù)加入到事件隊(duì)列的時(shí)候,會(huì)判斷隊(duì)列中是否還有未執(zhí)行的回調(diào),如果有的話,它就不會(huì)再往隊(duì)列中添加回調(diào)函數(shù)。 不然,會(huì)出現(xiàn)多個(gè)回調(diào)同時(shí)執(zhí)行的情況。
可以用下面的圖來概括:
總結(jié)
上面對javascript定時(shí)器執(zhí)行原理進(jìn)行了簡要的分析,希望能夠幫助我們更深入的理解javascript。文中有描述不當(dāng)?shù)牡胤娇梢栽谠u論中指出。
-
定時(shí)器
+關(guān)注
關(guān)注
23文章
3259瀏覽量
115925 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4355瀏覽量
63322 -
javascript
+關(guān)注
關(guān)注
0文章
525瀏覽量
54131
發(fā)布評論請先 登錄
相關(guān)推薦
定時(shí)器的基本工作原理
STM32定時(shí)器的工作原理及分類
定時(shí)器的工作原理
555定時(shí)器內(nèi)部框圖及電路工作原理

定時(shí)器/計(jì)數(shù)器的結(jié)構(gòu)和工作原理

評論