在js中如果打算使用setInterval進行倒數,計時等功能,往往是不准確的,因為setInterval的回調函數並不是到時后立即執行,而是等系統計算資源空閑下來后才會執行.而下一次觸發時間則是在setInterval回調函數執行完畢之后才開始計時,所以如果setInterval內執行的計算過於耗時,或者有其他耗時任務在執行,setInterval的計時會越來越不准,延遲很厲害.
下面的代碼可以說明這個問題
var startTime = new Date().getTime(); var count = 0; //耗時任務 setInterval(function(){ var i = 0; while(i++ < 100000000); }, 0); setInterval(function(){ count++; console.log(new Date().getTime() - (startTime + count * 1000)); }, 1000);
代碼里輸出了setInterval觸發時間和應該正確觸發時間的延遲毫秒數
176 340 495 652 807 961 1114 1268 1425 1579 1734 1888 2048 2201 2357 2521 2679 2834 2996 ......
可以看到延遲是越來越嚴重的.
為了在js里可以使用相對准確的計時功能,我們可以
var startTime = new Date().getTime(); var count = 0; setInterval(function(){ var i = 0; while(i++ < 100000000); }, 0); function fixed() { count++; var offset = new Date().getTime() - (startTime + count * 1000); var nextTime = 1000 - offset; if (nextTime < 0) nextTime = 0; setTimeout(fixed, nextTime); console.log(new Date().getTime() - (startTime + count * 1000)); } setTimeout(fixed, 1000);
代碼里,通過1000(也就是周期時間)減去當前時間和准確時間的差距,來算出下次觸發的時間,從而修正了當前觸發的延遲.
下面是輸出
186 200 230 271 158 899 900 899 900 899 899 899 902 899 418 202 232 266 145 174 192 214 242 268 149 179 214 ......
可以看到雖然觸發時間並非絕對准確,但由於每次觸發都進行及時修正,所以並沒有造成誤差積累.