首頁  >  文章  >  web前端  >  關於JavaScript中定時器的原理解析

關於JavaScript中定時器的原理解析

黄舟
黄舟原創
2017-11-18 13:53:481598瀏覽

在我們工作中很多人說到javascript中的計時器都會想到setTimeout() 和setInterval() 這兩個函數,但是卻不知道JavaScript中計時器的原理是什麼,那麼本文將從事件循環(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 的定義為:

週期性地呼叫一個函數(function)或執行一段程式碼。

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

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

由於javascript 的事件循環機制,導致第二個參數並不代表延遲delay毫秒之後立即執行回調函數,而是嘗試將回呼函數加入到事件隊列。實際上,setTimeout 和 setInterval 在這一點上處理又有區別:

  • setTimeout:延時delay毫秒之後,啥也不管,直接將回呼函數加入事件佇列。

  • setInterval: 延時delay毫秒之後,先看看事件佇列中是否存在還沒執行的回呼函數(setInterval的回呼函數),如果存在,就不要再往事件佇列裡加入回調函數了。

所以,當我們的程式碼中存在耗時的任務時,計時器並不會表現的如我們所想的那樣。

透過一個例子來理解

##下面的程式碼,本來希望能夠在 100ms 和 200ms 的時候(也就是剛好等待 100ms)呼叫回呼函數:

var timerStart1 = now();
setTimeout(function () {
 console.log('第一个setTimeout回调执行等待时间:', now() - timerStart1);
 var timerStart2 = now();
 setTimeout(function () {
  console.log('第二个setTimeout回调执行等待时间:', now() - timerStart2);
 }, 100);
}, 100);
// 输出:
// 第一个setTimeout回调执行等待时间: 106
// 第二个setTimeout回调执行等待时间: 107

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

var timerStart1 = now();
setTimeout(function () {
 console.log('第一个setTimeout回调执行等待时间:', now() - timerStart1);

 var timerStart2 = now();
 setTimeout(function () {
  console.log('第二个setTimeout回调执行等待时间:', now() - timerStart2);
 }, 100);

 heavyTask(); // 耗时任务
}, 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耗费时间: 1015
// 第一个setTimeout回调执行等待时间: 1018
// 第二个setTimeout回调执行等待时间: 1000

兩個 setTimeout 的等待事件由於耗時任務的存在不再是 100ms 了!讓我們來描述一下事情的經過:

  1. 首先,第一個耗時任務(heavyTask())開始執行,它需要大約 1000ms 才能執行完畢。

  2. 從耗時任務開始執行,過了 100ms, 第一個 setTimeout 的回呼函數期望執行,於是加入事件佇列,但此時前面的耗時任務還沒執行完,所以它只能在佇列中等待,直到耗時任務執行完畢它才開始執行,所以結果中我們開的看到的是: 第一個setTimeout回調執行等待時間: 1018。

  3. 第一個 setTimeout 回呼一執行,又開啟了第二個 setTimeout, 這個計時器也是期望延遲 100ms 之後能夠執行它的回呼函數。 但是,在第一個 setTimeout 又存在一個耗時任務,所有它的劇情跟第一個計時器一樣,也等待了 1000ms 才開始執行。

可以用下面的圖來概括:

#再來看setInterval 的一個例子:

var intervalStart = now();
setInterval(function () {
 console.log(&#39;interval距定义定时器的时间:&#39;, now() - loopStart);
}, 100);
var loopStart = now();
heavyTask();
console.log(&#39;heavyTask耗费时间:&#39;, 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 的區別, setInterval 在準備把回呼函數加入到事件佇列的時候,會判斷佇列中是否還有未執行的回調,如果有的話,它就不會再在佇列中加入回調函數。 不然,會出現多個回調同時執行的情況。

可以用下面的圖來概括:

#總結:

上面對javascript定時器執行原理進行了簡要的分析,相信小夥伴們對JavaScript定時器的工作原理有了一定的認識了,希望能夠幫助你。

相關推薦:

#Javascript定時器實例程式碼

##深入探索javascript定時器#

javascript定時器完整實例

#JavaScript計時器和最佳化的取消計時器方法

################################################### #

以上是關於JavaScript中定時器的原理解析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn