setTimeout()和setInterval() 什麼時候被調用執行

定義

setTimeout()和setInterval()常常被用來處理延時和定時任務。setTimeout() 方法用於在指定的毫秒數後調用函數或計算表達式,而setInterval()則能夠在每隔指定的毫秒數循環調用函數或表達式,直到clearInterval把它清除。瀏覽器

從定義上咱們能夠看到兩個函數十分相似,只不過前者執行一次,然後者能夠執行屢次,兩個函數的參數也相同,第一個參數是要執行的code或句柄,第二個是延遲的毫秒數。app

很簡單的定義,使用起來也很簡單,但有時候咱們的代碼並非按照咱們的想象精確時間被調用的,很讓人困惑函數

簡單示例

看個簡單的例子,簡單頁面在加載完兩秒後,寫下Delayed alert!spa

setTimeout('document.write("Delayed alert!");', 2000);

看起來很合理,咱們再看個setInterVal()方法的例子線程

複製代碼
var num = 0;
        var i = setInterval(function() {
            num++;
            var date = new Date();
            document.write(date.getMinutes() + ':' + date.getSeconds() + ':' + date.getMilliseconds() + '<br>');
            if (num > 10)
                clearInterval(i);
        }, 1000);
複製代碼

頁面每隔1秒記錄一次當前時間(分鐘:秒:毫秒),記錄十次後清除,再也不記錄。考慮到代碼執行時間可能記錄的不是執行時間,但時間間隔應該是同樣的,看看結果code

複製代碼
43:38:116
43:39:130
43:40:144
43:41:158
43:42:172
43:43:186
43:44:200
43:45:214
43:46:228
43:47:242
43:48:256
複製代碼

 

爲何

時間間隔幾乎是1000毫秒,但不精確,這是爲何呢?緣由在於咱們對JavaScript定時器存在一個誤解,JavaScript實際上是運行在單線程的環境中的,這就意味着定時器僅僅是計劃代碼在將來的某個時間執行,而具體執行時機是不能保證的,由於頁面的生命週期中,不一樣時間可能有其餘代碼在控制JavaScript進程。在頁面下載完成後代碼的運行、事件處理程序、Ajax回調函數都是使用一樣的線程,實際上瀏覽器負責進行排序,指派某段程序在某個時間點運行的優先級blog

咱們把效果放大一下看看,添加一個耗時的任務排序

複製代碼
function test() {
            for (var i = 0; i < 500000; i++) {
                var div = document.createElement('div');
                div.setAttribute('id', 'testDiv');
                document.body.appendChild(div);
                document.body.removeChild(div);
            }
        }
        setInterval(test, 10);
        var num = 0;
        var i = setInterval(function() {
            num++;
            var date = new Date();
            document.write(date.getMinutes() + ':' + date.getSeconds() + ':' + date.getMilliseconds() + '<br>');
            if (num > 10)
                clearInterval(i);
        }, 1000);
複製代碼

咱們又加入了一個定時任務,看看結果生命週期

複製代碼
47:9:222
47:12:482
47:16:8
47:19:143
47:22:631
47:25:888
47:28:712
47:32:381
47:34:146
47:35:565
47:37:406
複製代碼

這下效果明顯了,差距甚至都超過了3秒,並且差距很不一致。隊列

咱們能夠能夠把JavaScript想象成在時間線上運行。當頁面載入的時候首先執行的是頁面生命週期後面要用的方法和變量聲明和數據處理,在這以後JavaScript進程將等待更多代碼執行。當進程空閒的時候,下一段代碼會被觸發

除了主JavaScript進程外,還須要一個在進程下一次空閒時執行的代碼隊列。隨着頁面生命週期推移,代碼會按照執行順序添加入隊列,例如當按鈕被按下的時候他的事件處理程序會被添加到隊列中,並在下一個可能時間內執行。在接到某個Ajax響應時,回調函數的代碼會被添加到隊列。JavaScript中沒有任何代碼是當即執行的,但一旦進程空閒則儘快執行。定時器對隊列的工做方式是當特定時間過去後將代碼插入,這並不意味着它會立刻執行,只能表示它儘快執行。

知道了這些後,咱們就能明白,若是想要精確的時間控制,是不能依賴於JavaScript的setTimeout函數的。

重複的定時器

使用 setInterval() 建立的定時器可使代碼循環執行,到有指定效果的時候,清除interval就能夠,以下例

複製代碼
var my_interval = setInterval(function () {
            if (condition) {
                //..........
            } else {
                clearInterval(my_interval);
            }
        }, 100);
複製代碼

但這個方式的問題在於定時器的代碼可能在代碼再次被添加到隊列以前尚未執行完成,結果致使循環內的判斷條件不許確,代碼多執行幾回,之間沒有停頓。不過JavaScript已經解決這個問題,當使用setInterval()時,僅當沒有該定時器的其餘代碼實例時纔將定時器代碼插入隊列。這樣確保了定時器代碼加入到隊列的最小時間間隔爲指定間隔。

這樣的規則帶來兩個問題

  1. 1. 某些間隔會被跳過
  2. 2.多個定時器的代碼執行之間的間隔可能比預期要小

爲了不這兩個缺點,咱們可使用setTimeout()來實現重複的定時器

setTimeout(function () {
            //code
            setTimeout(arguments.callee, interval);
        }, interval)

這樣每次函數執行的時候都會建立一個新的定時器,第二個setTimeout()調用使用了agrument.callee 來獲取當前實行函數的引用,並設置另一個新定時器。這樣作能夠保證在代碼執行完成前不會有新的定時器插入,而且下一次定時器代碼執行以前至少要間隔指定時間,避免連續運行。

複製代碼
setTimeout(function () {
            var div = document.getElementById('moveDiv');
            var left = parseInt(div.style.left) + 5;
            div.style.left = left + 'px';
            if (left < 200) {
                setTimeout(arguments.callee, 50);
            }
        }, 50);
複製代碼

這段定時器代碼每次執行的時候,把一個div向右移動5px,當座標大於200的時候中止。

相關文章
相關標籤/搜索