javascript 定時器工作原理分析_javascript技巧

來源:互聯網
上載者:User

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('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 的區別, setInterval 在準備把回呼函數加入到事件隊列的時候,會判斷隊列中是否還有未執行的回調,如果有的話,它就不會再往隊列中添加回呼函數。 不然,會出現多個回調同時執行的情況。

可以用下面的圖來概括:

總結

上面對javascript定時器執行原理進行了簡要的分析,希望能夠協助我們更深入的理解javascript。文中有描述不當的地方可以在評論中指出。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.