最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

javascript 定時器工作原理分析

 更新時間:2016年12月03日 23:09:59   作者:mcbird  
說到 javascript 中的定時器,我們肯定會想到 setTimeout() 和 setInterval() 這兩個函數(shù)。本文將從 事件循環(huán)(Event Loop) 的角度來分析兩者的工作原理和區(qū)別

setTimeout()

MDNsetTimeout 的定義為:

在指定的延遲時間之后調(diào)用一個函數(shù)或執(zhí)行一個代碼片段。

語法

setTimeout 的語法非常簡單,第一個參數(shù)為回調(diào)函數(shù),第二個參數(shù)為延時的時間。函數(shù)返回一個數(shù)值類型的ID唯一標示符,此ID可以用作 clearTimeout 的參數(shù)來取消定時器:

var timeoutID = window.setTimeout(code, delay);

IE0+ 還支持回調(diào)參數(shù)的傳入:

var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);

setInterval()

MDN 對 setInterval 的定義為:

周期性地調(diào)用一個函數(shù)(function)或者執(zhí)行一段代碼。

由于 setInterval 和 setTimeout 的用法一樣,所以這里不再列出。

對第二個參數(shù)(delay)的說明

由于javascript 的事件循環(huán)機制,導致第二個參數(shù)并不代表延遲delay毫秒之后立即執(zhí)行回調(diào)函數(shù),而是嘗試將回調(diào)函數(shù)加入到事件隊列。實際上,setTimeout 和 setInterval 在這一點上處理又存在區(qū)別:

  • setTimeout:延時delay毫秒之后,啥也不管,直接將回調(diào)函數(shù)加入事件隊列。
  • setInterval: 延時delay毫秒之后,先看看事件隊列中是否存在還沒有執(zhí)行的回調(diào)函數(shù)(setInterval的回調(diào)函數(shù)),如果存在,就不要再往事件隊列里加入回調(diào)函數(shù)了。

所以,當我們的代碼中存在耗時的任務(wù)時,定時器并不會表現(xiàn)的如我們所想的那樣。

通過一個例子來理解

下面的代碼,本來希望能夠在 100ms 和 200ms 的時候(也就是剛好等待 100ms)調(diào)用回調(diào)函數(shù):

var timerStart1 = now();
setTimeout(function () {
 console.log('第一個setTimeout回調(diào)執(zhí)行等待時間:', now() - timerStart1);

 var timerStart2 = now();
 setTimeout(function () {
  console.log('第二個setTimeout回調(diào)執(zhí)行等待時間:', now() - timerStart2);
 }, 100);
}, 100);
// 輸出:
// 第一個setTimeout回調(diào)執(zhí)行等待時間: 106
// 第二個setTimeout回調(diào)執(zhí)行等待時間: 107

這樣的結(jié)果看上去正是我們所想的那樣,但是一旦我們在代碼中加入了耗時的任務(wù)時候,結(jié)果就不像我們所期望的那樣了:

var timerStart1 = now();
setTimeout(function () {
 console.log('第一個setTimeout回調(diào)執(zhí)行等待時間:', now() - timerStart1);

 var timerStart2 = now();
 setTimeout(function () {
  console.log('第二個setTimeout回調(diào)執(zhí)行等待時間:', now() - timerStart2);
 }, 100);

 heavyTask(); // 耗時任務(wù)
}, 100);

var loopStart = now();
heavyTask(); // 耗時任務(wù)
console.log('heavyTask耗費時間:', now() - loopStart);

function heavyTask() {
 var s = now();
 while(now() - s < 1000) {
 }
}

function now () {
 return new Date();
}
// 輸出:
// heavyTask耗費時間: 1015
// 第一個setTimeout回調(diào)執(zhí)行等待時間: 1018
// 第二個setTimeout回調(diào)執(zhí)行等待時間: 1000

兩個 setTimeout 的等待事件由于耗時任務(wù)的存在不再是 100ms 了!我們來描述一下事情的經(jīng)過:

  1. 首先,第一個耗時任務(wù)(heavyTask())開始執(zhí)行,它需要大約 1000ms 才能執(zhí)行完畢。
  2. 從耗時任務(wù)開始執(zhí)行,過了 100ms, 第一個 setTimeout 的回調(diào)函數(shù)期望執(zhí)行,于是被加入到事件隊列,但是此時前面的耗時任務(wù)還沒執(zhí)行完,所以它只能在隊列中等待,直到耗時任務(wù)執(zhí)行完畢它才開始執(zhí)行,所以結(jié)果中我們開的看到的是: 第一個setTimeout回調(diào)執(zhí)行等待時間: 1018。
  3. 第一個 setTimeout 回調(diào)一執(zhí)行,又開啟了第二個 setTimeout, 這個定時器也是期望延時 100ms 之后能夠執(zhí)行它的回調(diào)函數(shù)。 但是,在第一個 setTimeout 又存在一個耗時任務(wù),所有它的劇情跟第一個定時器一樣,也等待了 1000ms 才開始執(zhí)行。

可以用下面的圖來概括:

再來看 setInterval 的一個例子:

var intervalStart = now();
setInterval(function () {
 console.log('interval距定義定時器的時間:', now() - loopStart);
}, 100);

var loopStart = now();
heavyTask();
console.log('heavyTask耗費時間:', now() - loopStart);

function heavyTask() {
 var s = now();
 while(now() - s < 1000) {
 }
}

function now () {
 return new Date();
}
// 輸出:
// heavyTask耗費時間: 1013
// interval距定義定時器的時間: 1016
// interval距定義定時器的時間: 1123
// interval距定義定時器的時間: 1224

上面這段代碼,我們期望每隔 100ms 就打出一條日志。相對于 setTimeout 的區(qū)別, setInterval 在準備把回調(diào)函數(shù)加入到事件隊列的時候,會判斷隊列中是否還有未執(zhí)行的回調(diào),如果有的話,它就不會再往隊列中添加回調(diào)函數(shù)。 不然,會出現(xiàn)多個回調(diào)同時執(zhí)行的情況。

可以用下面的圖來概括:

總結(jié)

上面對javascript定時器執(zhí)行原理進行了簡要的分析,希望能夠幫助我們更深入的理解javascript。文中有描述不當?shù)牡胤娇梢栽谠u論中指出。

相關(guān)文章

最新評論

体育| 三河市| 株洲县| 通海县| 淳化县| 察隅县| 合阳县| 梧州市| 宾阳县| 贵州省| 文成县| 贡山| 广昌县| 五寨县| 瓮安县| 江西省| 卓资县| 个旧市| 云和县| 萨嘎县| 通州市| 奈曼旗| 英山县| 砀山县| 博白县| 定西市| 新民市| 正阳县| 农安县| 南溪县| 泾源县| 青州市| 天全县| 永昌县| 鄄城县| 道真| 会理县| 镇巴县| 阿勒泰市| 英超| 正蓝旗|