1. 程式人生 > >HTML5前端教程分享:JavaScript定時器

HTML5前端教程分享:JavaScript定時器

JS的定時器目前有三個:setTimeout、setInterval和setImmediate。

定時器也是一種非同步任務,通常瀏覽器都有一個獨立的定時器模組,定時器的延遲時間就由定時器模組來管理,當某個定時器到了可執行狀態,就會被加入主執行緒佇列。

JS定時器非常實用,做動畫的肯定都用到過,也是最常用的非同步模型之一。

有時候一些奇奇怪怪的問題,加一個setTimeout(fn, 0)(以下簡寫setTimeout(0))就解決了。不過,如果對定時器本身不熟悉,也會產生一些奇奇怪怪的問題。

setTimeout

setTimeout(fn, x)表示延遲x毫秒之後執行fn。

使用的時候千萬不要太相信預期,延遲的時間嚴格來說總是大於x毫秒的,至於大多少就要看當時JS的執行情況了。

另外,多個定時器如不及時清除(clearTimeout),會存在干擾,使延遲時間更加捉摸不透。所以,不管定時器有沒有執行完,及時清除已經不需要的定時器是個好習慣。

HTML5規範規定最小延遲時間不能小於4ms,即x如果小於4,會被當做4來處理。 不過不同瀏覽器的實現不一樣,比如,Chrome可以設定1ms,IE11/Edge是4ms。

setTimeout註冊的函式fn會交給瀏覽器的定時器模組來管理,延遲時間到了就將fn加入主程序執行佇列,如果佇列前面還有沒有執行完的程式碼,則又需要花一點時間等待才能執行到fn,所以實際的延遲時間會比設定的長。如在fn之前正好有一個超級大迴圈,那延遲時間就不是一丁點了。

(function testSetTimeout() {

const label = 'setTimeout';

console.time(label);

setTimeout(() => {

console.timeEnd(label);

}, 10);

for(let i = 0; i < 100000000; i++) {}

})();

結果是:setTimeout: 335.187ms,遠遠不止10ms。

setInterval

setInterval的實現機制跟setTimeout類似,只不過setInterval是重複執行的。

對於setInterval(fn, 100)容易產生一個誤區:並不是上一次fn執行完了之後再過100ms才開始執行下一次fn。 事實上,setInterval並不管上一次fn的執行結果,而是每隔100ms就將fn放入主執行緒佇列,而兩次fn之間具體間隔多久就不一定了,跟setTimeout實際延遲時間類似,和JS執行情況有關。

(function testSetInterval() {

let i = 0;

const start = Date.now();

const timer = setInterval(() => {

i += 1;

i === 5 && clearInterval(timer);

console.log(`第${i}次開始`, Date.now() - start);

for(let i = 0; i < 100000000; i++) {}

console.log(`第${i}次結束`, Date.now() - start);

}, 100);

})();

輸出

1

2

3

4

5

6

7

8

9

10

第1次開始 100

第1次結束

1089

第2次開始 1091

第2次結束

1396

第3次開始 1396

第3次結束

1701

第4次開始 1701

第4次結束

2004

第5次開始 2004

第5次結束

2307

可見,雖然每次fn執行時間都很長,但下一次並不是等上一次執行完了再過100ms才開始執行的,實際上早就已經等在佇列裡了。

另外可以看出,當setInterval的回撥函式執行時間超過了延遲時間,已經完全看不出有時間間隔了。

如果setTimeout和setInterval都在延遲100ms之後執行,那麼誰先註冊誰就先執行回撥函式。

setImmediate

這算一個比較新的定時器,目前IE11/Edge支援、Nodejs支援,Chrome不支援,其他瀏覽器未測試。

從API名字來看很容易聯想到setTimeout(0),不過setImmediate應該算是setTimeout(0)的替代版。

在IE11/Edge中,setImmediate延遲可以在1ms以內,而setTimeout有最低4ms的延遲,所以setImmediate比setTimeout(0)更早執行回撥函式。不過在Nodejs中,兩者誰先執行都有可能,原因是Nodejs的事件迴圈和瀏覽器的略有差異。

(function testSetImmediate() {

const label = 'setImmediate';
console.time(label);
setImmediate(() => {

console.timeEnd(label);

});

})();

Edge輸出:setImmediate: 0.555 毫秒

很明顯,setImmediate設計來是為保證讓程式碼在下一次事件迴圈執行,以前setTimeout(0)這種不可靠的方式可以丟掉了。

總結:

以上內容都是查閱資料得來,我們目前只瞭解一個setInterval計時器。有興趣的可以瞭解一下。

編輯:千鋒HTML5