setInterval 不準(zhǔn)的原因及問(wèn)題解決方案
setInterval 是 JavaScript 中用于定時(shí)執(zhí)行任務(wù)的常用方法。它的基本語(yǔ)法如下:
const intervalId = setInterval(callback, delay, ...args);
- callback 是要執(zhí)行的函數(shù)。
- delay 是每次執(zhí)行之間的時(shí)間間隔(以毫秒為單位)。
- args 是傳遞給 callback 的附加參數(shù)。
但是,在實(shí)際使用中,可能會(huì)發(fā)現(xiàn) setInterval 并不總是精確地按照預(yù)期的間隔時(shí)間來(lái)執(zhí)行任務(wù)。這是因?yàn)?JavaScript 中的定時(shí)器并不是絕對(duì)精準(zhǔn)的。
1. 為什么定時(shí)器不精準(zhǔn)?
1.1 單線程執(zhí)行
JavaScript 是單線程的,這意味著所有的任務(wù)都是在一個(gè)線程上排隊(duì)執(zhí)行的。雖然通過(guò) setInterval 設(shè)置了定時(shí)器,想要周期性地執(zhí)行某個(gè)任務(wù),但如果在回調(diào)執(zhí)行時(shí)主線程正在處理其他任務(wù)(比如執(zhí)行計(jì)算、渲染 UI、處理用戶事件等),就會(huì)導(dǎo)致回調(diào)函數(shù)被延遲執(zhí)行,從而使定時(shí)器間隔時(shí)間不準(zhǔn)確。
舉個(gè) ??:設(shè)置了一個(gè)定時(shí)器,每隔 100ms 執(zhí)行一次回調(diào)。
let count = 0; const startTime = Date.now(); console.log(`Start time: ${startTime} ms`); const intervalId = setInterval(() => { count++; console.log(`Callback executed: ${count}, Time: ${Date.now() - startTime} ms`); }, 100); // 這里模擬一個(gè)會(huì)占用主線程的長(zhǎng)任務(wù) setTimeout(() => { console.log(`Long task starting... Time: ${Date.now() - startTime} ms`); let start = Date.now(); while (Date.now() - start < 500) {} // 占用 500ms 的時(shí)間 console.log(`Long task finished, Time: ${Date.now() - startTime} ms`); }, 0);
事件流和時(shí)間線分析:
t = 0ms:開(kāi)始時(shí)刻,setInterval 在 100ms 后首次觸發(fā),然后執(zhí)行 setTimeout 長(zhǎng)任務(wù)。
t = 500ms:長(zhǎng)任務(wù)結(jié)束,主線程被釋放,但主線程的阻塞導(dǎo)致 setInterval 的回調(diào)沒(méi)有按預(yù)定時(shí)間執(zhí)行。
t = 100ms:setInterval 第一次回調(diào),但由于主線程被阻塞,回調(diào)沒(méi)有執(zhí)行。
t = 600ms:setInterval 執(zhí)行下一次回調(diào)。后面的 callback 也對(duì)應(yīng)推遲。
輸出日志:
主線程被占用時(shí),定時(shí)器回調(diào)會(huì)被推遲,造成時(shí)間間隔的不準(zhǔn)確。這種情況發(fā)生在主線程繁忙時(shí),任務(wù)執(zhí)行排隊(duì),定時(shí)器回調(diào)會(huì)“排隊(duì)”到后面,等待后續(xù)執(zhí)行。
1.2 任務(wù)排隊(duì)和事件循環(huán)
JavaScript 使用事件循環(huán)來(lái)調(diào)度任務(wù)。setInterval 的回調(diào)是被放入 宏任務(wù)隊(duì)列 中的,而宏任務(wù)隊(duì)列的執(zhí)行是有優(yōu)先級(jí)的,只有在當(dāng)前執(zhí)行棧為空時(shí),才會(huì)去執(zhí)行隊(duì)列中的任務(wù)。如果在執(zhí)行回調(diào)時(shí)有其他更優(yōu)先的任務(wù)(如 UI 渲染、用戶輸入處理等),那么定時(shí)器回調(diào)可能會(huì)被延遲。
舉個(gè) ??
let count = 0; const startTime = Date.now(); // 設(shè)置一個(gè)定時(shí)器,每 100ms 執(zhí)行一次回調(diào) const intervalId = setInterval(() => { count++; console.log(`setInterval callback executed: ${count}`); }, 100); // 模擬高優(yōu)先級(jí)任務(wù) console.log('Start of the script execution,time:', Date.now() - startTime); // 使用 Promise 模擬一個(gè)高優(yōu)先級(jí)任務(wù) Promise.resolve().then(() => { console.log('Promise microtask started,time:', Date.now() - startTime); // 模擬一些耗時(shí)的同步操作 let start = Date.now(); while (Date.now() - start < 300) {} // 阻塞主線程 300ms console.log('Promise microtask finished,time:', Date.now() - startTime); }); // 模擬低優(yōu)先級(jí)的異步任務(wù) setTimeout(() => { console.log('setTimeout task executed,time:', Date.now() - startTime); }, 50); console.log('End of the script execution,time:', Date.now() - startTime);
微隊(duì)列優(yōu)先執(zhí)行,導(dǎo)致 setInterval 和 setTimeout 的回調(diào)被推遲。
1.3 回調(diào)執(zhí)行時(shí)間的累積誤差
setInterval 設(shè)定的間隔時(shí)間只是開(kāi)始和結(jié)束之間的期望時(shí)間,但回調(diào)函數(shù)的執(zhí)行時(shí)間會(huì)影響下一個(gè)回調(diào)的執(zhí)行時(shí)間。如果回調(diào)函數(shù)本身執(zhí)行時(shí)間較長(zhǎng),那么定時(shí)器的間隔就會(huì)被推遲。
舉個(gè) ??
let count = 0; const startTime = Date.now(); const intervalId = setInterval(() => { count++; console.log(`Callback executed: ${count}, time: ${Date.now() - startTime}`); // 模擬一個(gè)需要較長(zhǎng)時(shí)間的操作 let start = Date.now(); while (Date.now() - start < 100) {} // 長(zhǎng)時(shí)間的計(jì)算任務(wù)(100ms) }, 50);
解釋:定時(shí)器第一次觸發(fā)時(shí),回調(diào)開(kāi)始執(zhí)行,100ms 后 while 循環(huán)才結(jié)束,所以下一個(gè)回調(diào)被推遲。新的定時(shí)器回調(diào)只能在上一個(gè)回調(diào)結(jié)束后才會(huì)被執(zhí)行,后續(xù)回調(diào)也會(huì)相應(yīng)推遲。
當(dāng)回調(diào)函數(shù)本身執(zhí)行占用的時(shí)間太長(zhǎng),它會(huì)影響到下一個(gè)回調(diào)的執(zhí)行時(shí)間,導(dǎo)致實(shí)際的回調(diào)間隔變得比預(yù)期更長(zhǎng)。因此,定時(shí)器的間隔時(shí)間不僅僅是設(shè)定的期望時(shí)間,回調(diào)函數(shù)本身的執(zhí)行時(shí)間也會(huì)對(duì)實(shí)際間隔產(chǎn)生影響。
1.4 最小時(shí)間間隔為 4ms
為什么存在 4ms 的最小時(shí)間限制?
當(dāng)嵌套 5 層以上的定時(shí)器時(shí),瀏覽器會(huì)因?yàn)闀r(shí)間精度和性能原因,將最小執(zhí)行間隔限制為 4 ms。這是一種性能優(yōu)化措施,目的是防止過(guò)于頻繁的定時(shí)器調(diào)用占用過(guò)多的計(jì)算資源,導(dǎo)致頁(yè)面的響應(yīng)變慢或無(wú)響應(yīng)。這個(gè) 4 ms 的限制對(duì)于大多數(shù)常見(jiàn)的 Web 應(yīng)用程序來(lái)說(shuō)已經(jīng)足夠使用。
舉個(gè) ??
// 試圖設(shè)置 1ms 間隔 let count = 0; const startTime = Date.now(); const intervalId = setInterval(() => { count++; if (count === 100) { console.log('100 times executed', Date.now() - startTime); clearInterval(intervalId); } }, 1); // 這里實(shí)際上會(huì)以 4ms 或更長(zhǎng)的間隔執(zhí)行
如果多個(gè)定時(shí)器同時(shí)設(shè)置為非常小的時(shí)間間隔(如 1ms),瀏覽器可能會(huì)把這些定時(shí)器合并成更大的間隔(如 4ms)。因此,setInterval 的間隔時(shí)間不會(huì)精確到設(shè)置的時(shí)間,尤其在高頻率執(zhí)行時(shí)。
1.5 失活頁(yè)面強(qiáng)制調(diào)整到 1s
這種優(yōu)化通常稱為 頁(yè)面不可見(jiàn)時(shí)定時(shí)器凍結(jié)。它意味著當(dāng)瀏覽器頁(yè)面不處于前臺(tái)(如切換標(biāo)簽頁(yè)或最小化瀏覽器窗口時(shí)),瀏覽器會(huì)自動(dòng)降低定時(shí)器的執(zhí)行頻率和精度,來(lái)節(jié)省資源并提升性能。例如,setInterval 的最小間隔時(shí)間可能會(huì)從 100 毫秒變成 1 秒,甚至更長(zhǎng)。
具體原因:在瀏覽器后臺(tái),頁(yè)面不再主動(dòng)渲染,用戶的交互也被暫停,因此瀏覽器會(huì)降低后臺(tái)頁(yè)面的計(jì)算和渲染頻率,以減少資源消耗。當(dāng)頁(yè)面失活時(shí),setInterval 的頻率會(huì)被降低,以減少對(duì) CPU 和內(nèi)存的占用。
let count = 0; let intervalId = setInterval(() => { count++; console.log(`回調(diào) #${count} 執(zhí)行, 時(shí)間: ${new Date().toLocaleTimeString()}`); }, 100); // 設(shè)置為 100 毫秒間隔 // 在 3 秒后清除定時(shí)器 setTimeout(() => { clearInterval(intervalId); console.log("定時(shí)器已停止"); }, 3000);
2. 如何實(shí)現(xiàn)更精準(zhǔn)的定時(shí)器?
以下是幾種常用的方法:
2.1 使用 requestAnimationFrame(針對(duì)動(dòng)畫(huà))
requestAnimationFrame 是一種專門(mén)為動(dòng)畫(huà)設(shè)計(jì)的定時(shí)器方法,與 setInterval 和 setTimeout 不同,requestAnimationFrame 在瀏覽器的渲染周期內(nèi)執(zhí)行回調(diào),因此,能提供比 setInterval 更加精準(zhǔn)的時(shí)間間隔,特別適用于動(dòng)畫(huà)或視覺(jué)渲染任務(wù)。
原理:
- requestAnimationFrame 會(huì)確?;卣{(diào)函數(shù)在下一次重繪之前執(zhí)行,并且在瀏覽器處于后臺(tái)時(shí)會(huì)自動(dòng)暫停,從而節(jié)省資源。
- 由于 requestAnimationFrame 是與瀏覽器的刷新率綁定的,它的執(zhí)行頻率與瀏覽器的刷新率(通常是每秒 60 次,即 16.67 毫秒)同步。因此它能在不同的瀏覽器和設(shè)備上提供更一致的執(zhí)行間隔。
舉個(gè) ??:實(shí)現(xiàn)一個(gè)簡(jiǎn)單的動(dòng)畫(huà),讓一個(gè)小方塊在屏幕上移動(dòng)。
使用 setInterval
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>setInterval vs requestAnimationFrame</title> <style> body { margin: 0; overflow: hidden; background-color: #f0f0f0; } .box { position: absolute; width: 50px; height: 50px; background-color: red; } </style> </head> <body> <div class="box"></div> <script> const box = document.querySelector('.box'); let posX = 0; // 使用 setInterval 來(lái)創(chuàng)建動(dòng)畫(huà) setInterval(() => { posX += 2; if (posX > window.innerWidth) { posX = -50; } box.style.transform = `translateX(${posX}px)`; }, 16); // 約等于每秒 60 幀 </script> </body> </html>
示例中,我們?cè)O(shè)置了定時(shí)器每 16 ms 執(zhí)行一次回調(diào),這大約是每秒 60 次(60 FPS)。但是由于 setInterval 是基于固定時(shí)間間隔的,它不能保證每次回調(diào)執(zhí)行時(shí)都能與瀏覽器的渲染周期同步。如果回調(diào)執(zhí)行得太慢,或者頁(yè)面有其他任務(wù)需要處理,定時(shí)器可能會(huì)出現(xiàn)間隔不準(zhǔn)的情況,導(dǎo)致動(dòng)畫(huà)卡頓。
使用 requestAnimationFrame
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>setInterval vs requestAnimationFrame</title> <style> body { margin: 0; overflow: hidden; background-color: #f0f0f0; } .box { position: absolute; width: 50px; height: 50px; background-color: red; } </style> </head> <body> <div class="box"></div> <script> const box = document.querySelector('.box'); let posX = 0; // 使用 requestAnimationFrame 來(lái)創(chuàng)建動(dòng)畫(huà) function animate() { posX += 2; if (posX > window.innerWidth) { posX = -50; } box.style.transform = `translateX(${posX}px)`; requestAnimationFrame(animate); // 每次動(dòng)畫(huà)完成后請(qǐng)求下一幀 } requestAnimationFrame(animate); // 啟動(dòng)動(dòng)畫(huà) </script> </body> </html>
示例中,回調(diào)函數(shù)被安排在瀏覽器下次重繪前執(zhí)行。由于它與瀏覽器的渲染周期同步,它能確保每幀動(dòng)畫(huà)的執(zhí)行間隔更精確,而且通常與瀏覽器的刷新率(一般是每秒 60 幀)一致。
更重要的是,當(dāng)頁(yè)面切換到后臺(tái)時(shí),requestAnimationFrame 會(huì)自動(dòng)暫停,減少資源消耗。而 setInterval 則不受影響,可能會(huì)繼續(xù)執(zhí)行,浪費(fèi)資源。
2.2 使用 Web Workers
如果需要一個(gè)不依賴瀏覽器渲染循環(huán)的精確定時(shí)器(例如處理后臺(tái)計(jì)算任務(wù)),可以使用 Web Workers。Web Workers 允許在后臺(tái)線程中運(yùn)行 JavaScript 代碼,而不會(huì)阻塞主線程。通過(guò) Web Workers 可以獲得更高精度的定時(shí)器控制,尤其適用于那些不涉及 UI 渲染的任務(wù)。
原理:
- Web Workers 不受瀏覽器的渲染影響,因此它們不會(huì)因?yàn)轫?yè)面切換到后臺(tái)而降低執(zhí)行頻率。
- 由于其與 UI 線程分離,Web Workers 的定時(shí)器可以維持更穩(wěn)定的間隔,避免了主線程中的限制。
舉個(gè) ??
場(chǎng)景:我們需要一個(gè)精確的定時(shí)器來(lái)執(zhí)行計(jì)算任務(wù),例如:每隔 50ms 進(jìn)行一次計(jì)算。這個(gè)任務(wù)是一個(gè)后臺(tái)計(jì)算任務(wù),與 UI 渲染無(wú)關(guān),而且希望它在頁(yè)面切換到后臺(tái)時(shí)不受影響。
1、創(chuàng)建 Web Worker 來(lái)處理后臺(tái)任務(wù)。
// worker.js // 接收到主線程的消息后執(zhí)行定時(shí)任務(wù) let intervalId; self.onmessage = function (e) { if (e.data === 'start') { // 啟動(dòng)定時(shí)器,每50毫秒執(zhí)行一次計(jì)算 intervalId = setInterval(() => { const result = Math.random(); // 模擬計(jì)算任務(wù)(例如生成隨機(jī)數(shù)) self.postMessage(result); // 將計(jì)算結(jié)果發(fā)送回主線程 }, 50); } else if (e.data === 'stop') { // 停止定時(shí)器 clearInterval(intervalId); } };
2、主線程代碼(index.html)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Web Worker</title> <style> body { margin: 0 auto; display: flex; justify-content: center; align-items: center; } .container { text-align: center; } #result { font-size: 24px; margin-top: 20px; } </style> </head> <body> <div class="container"> <h3>Web Worker 精確定時(shí)器</h3> <button id="startBtn">Start</button> <button id="stopBtn">Stop</button> <div id="result"></div> </div> <script> // 創(chuàng)建一個(gè)新的 Web Worker const worker = new Worker('worker.js'); const startBtn = document.getElementById('startBtn'); const stopBtn = document.getElementById('stopBtn'); // 顯示計(jì)算結(jié)果的元素 const resultDiv = document.getElementById('result'); // 監(jiān)聽(tīng) Web Worker 返回的數(shù)據(jù) worker.onmessage = function (e) { const result = e.data; resultDiv.innerText = `計(jì)算結(jié)果: ${result}`; }; // 啟動(dòng) Web Worker 中的定時(shí)任務(wù) startBtn.addEventListener('click', function () { worker.postMessage('start'); }); // 停止 Web Worker 中的定時(shí)任務(wù) stopBtn.addEventListener('click', function () { worker.postMessage('stop'); }); </script> </body> </html>
特點(diǎn):精確性、獨(dú)立性、穩(wěn)定性。
實(shí)際應(yīng)用:
這種精確的定時(shí)器非常適用于一些不涉及 UI 更新的任務(wù),如:
- 后臺(tái)數(shù)據(jù)計(jì)算任務(wù)(例如處理大量數(shù)據(jù)、定時(shí)抓取數(shù)據(jù)等)
- 數(shù)據(jù)同步(例如定時(shí)發(fā)送請(qǐng)求到服務(wù)器)
- 游戲引擎的后臺(tái)計(jì)算任務(wù)
- AI 算法或大數(shù)據(jù)分析等后臺(tái)任務(wù)
使用 Web Workers 可以確保這些任務(wù)的執(zhí)行頻率更加穩(wěn)定且不受主線程的干擾,非常適用于后臺(tái)計(jì)算任務(wù),不會(huì)因?yàn)轫?yè)面切換到后臺(tái)而降低性能。
2.3 使用 performance.now()
performance.now() 提供了比 Date.now() 更高精度的時(shí)間戳(微秒級(jí)別),適用于需要高精度計(jì)時(shí)的場(chǎng)景,比如高頻率任務(wù)或精細(xì)調(diào)度。與 setTimeout 或 setInterval 結(jié)合使用時(shí),能更精確地控制時(shí)間間隔。
原理:
- performance.now() 提供的時(shí)間戳是相對(duì)于頁(yè)面加載開(kāi)始的,而不是當(dāng)前時(shí)間,這使得它能夠在不同設(shè)備和系統(tǒng)中提供一致的精度。
- 因?yàn)?performance.now() 的精度更高,可以計(jì)算出每次調(diào)用的精確間隔,避免了定時(shí)器本身的偏差。
舉個(gè) ??
<div class="container"> <h3>高精度定時(shí)器</h3> <div id="result">計(jì)時(shí)中...</div> </div> <script> let lastTimestamp = performance.now(); // 初始化時(shí)間戳 let totalTime = 0; // 累積經(jīng)過(guò)的時(shí)間 let count = 0; // 計(jì)數(shù)器,記錄執(zhí)行次數(shù) function tick() { const now = performance.now(); const elapsed = now - lastTimestamp; // 計(jì)算兩次回調(diào)之間的時(shí)間間隔 lastTimestamp = now; totalTime += elapsed; count++; // 更新顯示的時(shí)間間隔 document.getElementById('result').innerHTML = ` 總執(zhí)行時(shí)間: ${totalTime.toFixed(2)} 毫秒<br> 執(zhí)行次數(shù): ${count} 次<br> 平均間隔: ${(totalTime / count).toFixed(2)} 毫秒 `; setTimeout(tick, 50); // 動(dòng)態(tài)調(diào)整下一次執(zhí)行的間隔 } // 啟動(dòng)高精度定時(shí)器 tick(); </script>
注意一點(diǎn):(totalTime / count) 和 elapsed 之間會(huì)出現(xiàn)偏差。原因如下:
1、時(shí)間累積誤差(Cumulative Error)
每次執(zhí)行回調(diào)時(shí),elapsed 計(jì)算的是當(dāng)前回調(diào)與上次回調(diào)之間的時(shí)間間隔,而 (totalTime / count) 是根據(jù)所有回調(diào)的累積時(shí)間來(lái)計(jì)算的平均間隔。
由于每次調(diào)用時(shí) elapsed 只是當(dāng)前執(zhí)行的時(shí)間差,而 (totalTime / count) 是逐步累加的,可能會(huì)隨著時(shí)間的推移引入微小的誤差,尤其是在高頻率任務(wù)中。
這類誤差累積可能使得兩個(gè)值之間的差異逐漸增大,尤其是在多次計(jì)算的情況下。
2、setTimeout 的不精確性
setTimeout 不能精確地按照指定的間隔時(shí)間執(zhí)行回調(diào),它會(huì)受到事件隊(duì)列的調(diào)度、CPU 負(fù)載、瀏覽器渲染周期等因素的影響。每次 setTimeout 的延遲可能會(huì)有所不同,導(dǎo)致實(shí)際的回調(diào)執(zhí)行時(shí)間存在波動(dòng)。這就會(huì)導(dǎo)致 elapsed 的值和 (totalTime / count) 之間產(chǎn)生差異。
例如,在高負(fù)載的情況下,回調(diào)可能會(huì)比預(yù)期的 50ms 晚幾毫秒才執(zhí)行,從而導(dǎo)致 elapsed 比實(shí)際的 50ms 值略大,而 (totalTime / count) 則會(huì)反映出一個(gè)平均值,減小了這種偏差。
3、調(diào)度隊(duì)列的延遲
在瀏覽器的事件循環(huán)(Event Loop)中,任務(wù)隊(duì)列中的回調(diào)會(huì)按照先后順序執(zhí)行。即使設(shè)置了 50 ms 的延遲,但回調(diào)的執(zhí)行也可能因?yàn)槠渌蝿?wù)(例如渲染、計(jì)算等)在任務(wù)隊(duì)列中排隊(duì)而延遲執(zhí)行。這意味著 elapsed 的計(jì)算不一定精確地反映實(shí)際的間隔時(shí)間,導(dǎo)致 (totalTime / count) 和 elapsed 出現(xiàn)偏差。
4、performance.now() 與 setTimeout 調(diào)度的差異
雖然 performance.now() 提供了高精度的時(shí)間戳,但它只能準(zhǔn)確地提供當(dāng)前的時(shí)間點(diǎn),而 setTimeout 并不是在精確的時(shí)間點(diǎn)執(zhí)行回調(diào),而是放入任務(wù)隊(duì)列中,并等待瀏覽器調(diào)度。
因此,實(shí)際的時(shí)間間隔(通過(guò) elapsed 計(jì)算的)可能會(huì)比理想的間隔稍長(zhǎng),尤其是在高負(fù)載或繁忙的瀏覽器中。
總結(jié),這些誤差是由瀏覽器的定時(shí)器實(shí)現(xiàn)機(jī)制所決定的,無(wú)法完全避免。但通過(guò)合理的設(shè)計(jì)和優(yōu)化,可以減小這種誤差對(duì)精度的影響。
2.4 避免重疊的 setTimeout 或 setInterval 調(diào)用
瀏覽器的定時(shí)器方法(如 setTimeout 或 setInterval)可能因?yàn)槿蝿?wù)隊(duì)列的積壓、事件循環(huán)的阻塞、或頁(yè)面切換等原因,導(dǎo)致實(shí)際回調(diào)執(zhí)行的間隔不準(zhǔn)確。
解決方法:
- 逐步遞歸調(diào)用:代替 setInterval 使用遞歸的 setTimeout,確保定時(shí)器按期望的間隔執(zhí)行,而不是固定的時(shí)間間隔。
- 調(diào)整回調(diào)中的邏輯:每次回調(diào)時(shí)都重新計(jì)算下一個(gè)定時(shí)器的執(zhí)行時(shí)間,而不是使用固定的時(shí)間間隔。
舉個(gè) ??
let lastTime = performance.now(); let count = 0; function recursiveTimeout() { const now = performance.now(); const elapsed = now - lastTime; if (elapsed >= 100) { // 精確的 100ms 間隔 count++; console.log(`回調(diào) #${count}, 時(shí)間: ${now}`); lastTime = now; } // 遞歸調(diào)用 setTimeout,確保間隔準(zhǔn)確 setTimeout(recursiveTimeout, 100 - (elapsed % 100)); } recursiveTimeout(); // 啟動(dòng)遞歸定時(shí)器
2.5 在 Node.js 中使用 setImmediate() 或 setInterval()
在 Node.js 環(huán)境中,setImmediate() 和 setInterval() 可以提供比普通的 setTimeout() 更高精度的定時(shí)器。
setImmediate() 用于在當(dāng)前事件循環(huán)結(jié)束后執(zhí)行回調(diào),通常比 setTimeout(fn, 0) 更精確。
3. 為什么需要精確的定時(shí)器?
精準(zhǔn)的定時(shí)器對(duì)于某些應(yīng)用至關(guān)重要,如:
- 動(dòng)畫(huà)渲染:動(dòng)態(tài)渲染的 UI 需要高精度的定時(shí)器來(lái)確保動(dòng)畫(huà)流暢。
- 游戲引擎:游戲引擎需要根據(jù)時(shí)間精確更新游戲邏輯。
- 實(shí)時(shí)通信:實(shí)時(shí)系統(tǒng)(如視頻通話、實(shí)時(shí)數(shù)據(jù)同步等)需要保證定時(shí)任務(wù)的準(zhǔn)確性,以確保數(shù)據(jù)的實(shí)時(shí)性和一致性。
通過(guò)優(yōu)化定時(shí)器的精度,可以提升應(yīng)用的流暢度、實(shí)時(shí)性和響應(yīng)速度,確保用戶體驗(yàn)不被延遲或不精確的定時(shí)器行為影響。
到此這篇關(guān)于setInterval 不準(zhǔn)的原因及問(wèn)題解決方案的文章就介紹到這了,更多相關(guān)setInterval 不準(zhǔn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript 操作cookies及正確使用cookies的屬性
在 JS(JavaScript) 操作cookies比較復(fù)雜,在 ASP 里面我們只需要知道 cookie 的名稱、cookie 的值就行了,而 JS 里面,我們面對(duì)的是 cookie 的字符串,你自己編寫(xiě)這個(gè)字符串寫(xiě)入客戶端,然后自己解析這個(gè)字符串。2009-10-10JavaScript中的this基本問(wèn)題實(shí)例小結(jié)
這篇文章主要介紹了JavaScript中的this基本問(wèn)題,結(jié)合實(shí)例形式總結(jié)分析了JavaScript中this的功能、常見(jiàn)用法與操作注意事項(xiàng),需要的朋友可以參考下2020-03-03詳解js實(shí)時(shí)獲取并顯示當(dāng)前時(shí)間的方法
這篇文章主要介紹了js實(shí)時(shí)獲取并顯示當(dāng)前時(shí)間的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05JS面向?qū)ο缶幊袒A(chǔ)篇(一) 對(duì)象和構(gòu)造函數(shù)實(shí)例詳解
這篇文章主要介紹了JS面向?qū)ο缶幊虒?duì)象和構(gòu)造函數(shù),結(jié)合實(shí)例形式詳細(xì)分析了JS面向?qū)ο缶幊虒?duì)象和構(gòu)造函數(shù)具體概念、原理、使用方法及操作注意事項(xiàng),需要的朋友可以參考下2020-03-03JavaScript 實(shí)現(xiàn)鍋拍灰太狼小游戲
這篇文章主要介紹了JavaScript 實(shí)現(xiàn)鍋拍灰太狼小游戲,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09基于JavaScript實(shí)現(xiàn)手機(jī)短信按鈕倒計(jì)時(shí)(超簡(jiǎn)單)
在淘寶等購(gòu)物網(wǎng)站,我們都會(huì)看到一個(gè)發(fā)送短信倒計(jì)時(shí)的按鈕,究竟是如何實(shí)現(xiàn)的呢?下面小編通過(guò)本篇文章給大家分享一段代碼關(guān)于js實(shí)現(xiàn)手機(jī)短信按鈕倒計(jì)時(shí),需要的朋友參考下2015-12-12