1. 程式人生 > >JavaScript 關於setTimeout與setInterval的小研究

JavaScript 關於setTimeout與setInterval的小研究

說明

在開發功能“軌跡播放”時,遇到了一個情況。
原先同事已經開發了一版,這次有個新功能:點選線上任意一點後可以從點選處重新播放。
看了一下原來的版本,發現同時使用了setTimeout和setInterval,兩者配合實現點線播放。
簡單結構如下

        function test() {
            setInterval(function () {
                console.log("interval");
                                //省略插值方法得到arr
                                (...)
                play(arr);
            }, 2000);
        }
        function play(arr) {
            setTimeout(function () {
                play(arr);
                console.log("setTimeout");
            }, 40);
        }

我覺得這個結構欠妥,兩個定時器配合必定會出現失誤!因此重構了一版,將兩個定時器改為一個,用setInterval解決。
但是此時我並不知道欠妥欠在什麼地方,缺乏理論支援,現在閒下來仔細研究了一下

找問題

在仔細研究了舊版本後,我先把舊版本結構扒了出來,排除其他因素,自己模擬了一個簡單版(就是上面的程式碼)
setTimeout:在執行時,是在載入後延遲指定時間後,去執行一次表示式,僅執行一次
setInterval:在執行時,它從載入後,每隔指定的時間就執行一次表示式

  • 實驗一:在使用setInterval和setTimeout方法上,並沒有什麼問題,決定跑一下,結果如下

從結果得出兩點結論

  1. setTimeout與setInterval並不是50倍速度配合執行著
  2. 兩次interval間,timeout執行的次數越來越多,表明setInterval執行間隔越來越長,延遲越來越大
  • 實驗二:加一點人工干預再執行
        function test() {
            setInterval(function () {
                console.log("interval");
                play();
            }, 2000);
        }
        function play() {
                    //延遲執行
            for (var i = 0; i < 100000000; i++) {
                
             }
            setTimeout(function () {
                play();
                console.log("setTimeout");
            }, 40);
        }

從結果得出兩點結論

  1. setInterval可能會隨函式處理時間,減少間隔
  2. 推測,因為Javascript是單執行緒的,setInterval和setTimeout是放佇列裡執行的,很容易受到回撥事件影響
  • 實驗三:拖動縮放瀏覽器

從結果得出結論

  1. 當瀏覽器標籤切換到其他頁面,或者瀏覽器最小化,會影響計時器,兩者會出現間隔減小

涉及知識點

綜上實驗結果,網上搜集了一些資料能說明問題:

  1. JavaScript是單執行緒,但是瀏覽器是多執行緒,Javascript是瀏覽器多執行緒中的一個執行緒。(圖參考自:https://www.cnblogs.com/tesky0125/p/4619549.html)

  1. Javascript會把執行的回撥函式、瀏覽器的觸發事件、UI渲染事件,先放到佇列中,佇列根據先進先出的規則,依次執行他們,當執行到佇列中的setInterval時很難保證其與setTimeout同步關係還保持。
  2. setInterval無視程式碼錯誤:程式碼報錯,但是setInterval依舊會按時執行,不會中斷。
  3. setInterval無視網路延遲:如果呼叫ajax或其他服務,他不會管是否返回回撥,會繼續按時執行。
  4. setInterval不保證執行:因為setInterval會定時執行,如果函式邏輯很長,間隔時間內執行不完,後續方法會被拋棄。
  5. 會受瀏覽器狀態影響,tab切換、最小化等

解決方案

在做軌跡播放時,setInterval的延遲還在可接受範圍之內,但是網上給出的最佳解決方案是用setTimeout做。
setTimeout只會執行一次,在執行完成後,重新啟動新的Timeout,時間runtime計算設定為差時,減少出現間隔越來越大的情況

        function test() {
                    //runTime,計算差時
                        runTime = 1000 - 執行耗時;
            setTimeout(callback, runTime);
        }
        setTimeout(test, 1000);