JavaScript 關於setTimeout與setInterval的小研究
阿新 • • 發佈:2019-11-27
說明
在開發功能“軌跡播放”時,遇到了一個情況。
原先同事已經開發了一版,這次有個新功能:點選線上任意一點後可以從點選處重新播放。
看了一下原來的版本,發現同時使用了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方法上,並沒有什麼問題,決定跑一下,結果如下
從結果得出兩點結論
- setTimeout與setInterval並不是50倍速度配合執行著
- 兩次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); }
從結果得出兩點結論
- setInterval可能會隨函式處理時間,減少間隔
- 推測,因為Javascript是單執行緒的,setInterval和setTimeout是放佇列裡執行的,很容易受到回撥事件影響
實驗三:拖動縮放瀏覽器
從結果得出結論
- 當瀏覽器標籤切換到其他頁面,或者瀏覽器最小化,會影響計時器,兩者會出現間隔減小
涉及知識點
綜上實驗結果,網上搜集了一些資料能說明問題:
- JavaScript是單執行緒,但是瀏覽器是多執行緒,Javascript是瀏覽器多執行緒中的一個執行緒。(圖參考自:https://www.cnblogs.com/tesky0125/p/4619549.html)
- Javascript會把執行的回撥函式、瀏覽器的觸發事件、UI渲染事件,先放到佇列中,佇列根據先進先出的規則,依次執行他們,當執行到佇列中的setInterval時很難保證其與setTimeout同步關係還保持。
- setInterval無視程式碼錯誤:程式碼報錯,但是setInterval依舊會按時執行,不會中斷。
- setInterval無視網路延遲:如果呼叫ajax或其他服務,他不會管是否返回回撥,會繼續按時執行。
- setInterval不保證執行:因為setInterval會定時執行,如果函式邏輯很長,間隔時間內執行不完,後續方法會被拋棄。
- 會受瀏覽器狀態影響,tab切換、最小化等
解決方案
在做軌跡播放時,setInterval的延遲還在可接受範圍之內,但是網上給出的最佳解決方案是用setTimeout做。
setTimeout只會執行一次,在執行完成後,重新啟動新的Timeout,時間runtime計算設定為差時,減少出現間隔越來越大的情況
function test() {
//runTime,計算差時
runTime = 1000 - 執行耗時;
setTimeout(callback, runTime);
}
setTimeout(test, 1000);