[javascript] 深入了解 setTimeout() 與 setInterval() 的不同之處

許多人認為 setTimeout() 與 setInterval() 的差別僅是一個僅會執行一次,另一個則會自動重複執行這樣的差別。

但其中其實還有許多值得深入與研究的不同之處,這篇將由淺至深地介紹兩者的差別。

  1. setTimeout()
    綁定在瀏覽器 window 的一個方法,可以透過 setTimeout 指定一段程式碼或函式在多少毫秒(ms)後執行,並回傳此定時器的編號。
    可以透過 clearTimeout 取消程式碼的執行。
    例如:

    // 函式會將第一個參數字串使用eval轉換為可執行之程式碼
    // 三秒後在 console 印出 "test123"
    setTimeout('console.log("test123");',3000);
    // 你也可以寫成function
    setTimeout(function(){console.log('test123');},3000);
    

    setTimeout()執行方法其實是將須執行程式碼加入任務佇列,直到輪到此程式碼執行時,檢查時間是否到達,若到達則執行程式碼。
    舉例來說:

    // 單純執行這段程式碼,可以看到實際所執行任務的時間
    var startTime=new Date();
    setTimeout(function(){console.log(new Date()-startTime);},100);
    

    接下來我們再測試以下程式碼

    var startTime=new Date();
    setTimeout(function(){console.log(new Date()-startTime);},100);
    for(var i=0; i<1000000000; i++){}
    

    可以發現 setTimeout 所設定的程式碼,會因為目前任務佇列所執行的程式碼而可能發生延誤執行的狀況。

    再來我們測試一段 setTimeout 執行時間的程式碼

    var startTime=new Date();
    var func = function(){
    console.log('start: ' + (new Date()-startTime));
    for(var i=0; i<1000000000; i++){};
    console.log('end: ' + (new Date()-startTime));
    setTimeout(func,100);
    };
    setTimeout(func,100);
    // start: 2515
    // end: 3457
    // start: 3558
    // end: 4503
    // start: 4604
    // end: 5543
    // ....
    

    上面這段程式碼,可以看到執行 func 的 endstart 時間基本上是符合我們所設定的 100 ms。(待會與下方 setInterval 比較)

  2. setInterval()
    綁定在瀏覽器 window 的一個方法,可以透過 setInterval 指定一段程式碼或函式定時在多少毫秒(ms)後執行,並回傳此定時器的編號。
    可以透過 clearInterval 取消程式碼的執行。
    大致用法與 setTimeout 相同,只差在定時執行,因此這邊我們同樣測試延遲執行的問題。

    var startTime=new Date();
    setInterval(function(){console.log(new Date()-startTime);},100);
    for(var i=0; i<1000000000; i++){}
    

    可以發現與 setTimeout 一樣是有延遲的狀況發生。

    接下來我們再測試以下程式碼

    var startTime=new Date();
    var func = function(){
    console.log('start: ' + (new Date()-startTime));
    for(var i=0; i<1000000000; i++){}
    console.log('end: ' + (new Date()-startTime));
    };
    setInterval(func,100);
    // start: 2520
    // end: 3465
    // start: 3466
    // end: 4409
    // start: 4409
    // end: 5350
    // start: 5351
    // end: 6291
    // start: 6292
    

    上面這段程式碼的執行結果,與上面的 setTimeout 比較,你會發現 setInterval 的 end 與 start 時間跳動非常大,並不是我們所設定的 100 ms。
    由於 setInterval 是一開始就標定了執行時間點,當所註冊的函式(func)超過執行的時間點,結束時則會馬上觸發(func),因此並不會是固定的 100 ms。

     

 

最後,利用前面提到的延遲執行(將 setTimeout 與 setInterval 事件放進 task queue)特性,我們可以應用在程式碼執行的順序(比如等 innerHTML 執行完才 document.getElementById)。
舉例來說:

setTimeout(function() {
console.log("想最後執行的一段程式碼(getElementById)");
}, 0);
function a(x) {
console.log("a() 開始 innerHTML");
b(x);
console.log("a() 結束 innerHTML");
}

function b(y) {
console.log("b() 開始");
console.log("傳入的值" + y);
console.log("b() 結束");
}
console.log("程式碼開始");
a(42);
console.log("程式碼結束");

// 執行結果:
// 程式碼開始
// a() 開始 innerHTML
// b() 開始
// 傳入的值42
// b() 結束
// a() 結束 innerHTML
// 程式碼結束
// 想最後執行的一段程式碼(getElementById)

 

參考資料:

http://www.jeffjade.com/2016/01/10/2016-01-10-javacript-setTimeout/

http://www.jeffjade.com/2016/01/10/2016-01-10-javaScript-setInterval/

Leave a Reply

Your email address will not be published. Required fields are marked *