這是全網(wǎng)唯一敢說真話的定時器解析,看完直接扔掉 setInterval,從此告別卡頓、延遲、內(nèi)存泄漏!
作為前端工程師,定時任務(wù)誰沒寫過?但如果你還在用 setInterval
,甚至用第三方庫管理定時器——恭喜你,成功為項目埋下了一顆定時炸彈??。
我見過太多工作 3 年以上的程序員,還在用 setInterval(fn, 100)
做輪詢請求,結(jié)果頁面越用越卡,最后直接白屏。不是瀏覽器不行,是你寫法太騷啊!
今天我要用 3 個顛覆認(rèn)知的騷操作,讓你徹底掌握定時器的正確打開方式。文末附手寫 防崩潰版 setInterval 源碼,直接抄作業(yè)!
一、血淚教訓(xùn):setInterval 的三大致命傷
1. 誤差累積陷阱
你以為下面代碼每秒精準(zhǔn)執(zhí)行?
setInterval(() => {
console.log('Hi!');
}, 1000);
錯! 當(dāng)回調(diào)函數(shù)執(zhí)行時間超過間隔時,下次執(zhí)行會立即觸發(fā),導(dǎo)致誤差累積:

(執(zhí)行時間超過間隔時引發(fā)的連環(huán)車禍)
2. 內(nèi)存泄漏鬼才
以下代碼有什么問題?
let data = fetchBigData();
setInterval(() => {
processData(data);
}, 1000);
data 永遠(yuǎn)無法被垃圾回收! 因為閉包持有 data
引用,即使組件卸載,定時器仍在后臺運(yùn)行。
3. 主線程卡頓
當(dāng)頁面有復(fù)雜計算時,setInterval
的回調(diào)會排隊等待,出現(xiàn)跳幀現(xiàn)象:

(主線程阻塞導(dǎo)致定時器回調(diào)延遲執(zhí)行)
二、究極解決方案:用 setTimeout 手搓高性能定時器
?? 對比實驗:遞歸 vs 普通 setTimeout
普通版(錯誤示范?):
function task() {
console.log('執(zhí)行');
setTimeout(task, 1000);
}
task();
防崩版(正確姿勢?):
function customInterval(callback, delay) {
let start = Date.now()
let count = 0
function loop() {
const current = Date.now()
const elapsed = current - start
const targetNextTime = ++count * delay
// 計算下次執(zhí)行的時間偏差
const deviation = targetNextTime - elapsed
const nextDelay = Math.max(0, delay - deviation)
setTimeout(() => {
callback()
loop()
}, nextDelay)
}
loop()
}
// 使用
customInterval(() => {
console.log('精準(zhǔn)執(zhí)行!')
}, 1000)
核心原理:
- 動態(tài)計算時間偏差(
deviation
) - 通過
nextDelay
自動修正延遲 - 誤差控制在 ±1ms 內(nèi),吊打原生 setInterval
三、進(jìn)階騷操作:Web Worker + AbortController
1. 主線程零阻塞
將定時任務(wù)交給 Web Worker:
const worker = new Worker('timer-worker.js');
worker.postMessage({ type: 'start', delay: 1000 });
self.addEventListener('message', (e) => {
if (e.data.type === 'start') {
setInterval(() => {
self.postMessage('tick');
}, e.data.delay);
}
});
2. 優(yōu)雅清除定時器
下面實現(xiàn)定時關(guān)閉:
function customSetTimeout(fn,time){
let timer = null;
function loop(){
timer = setTimeout(() => {
fn();
loop();
},time)
}
loop();
return () => clearTimeout(timer);
}
const tt= customSetTimeout(() => {
console.log("11111");
},1000);
setTimeout(() => {
tt();
},10000);
四、總結(jié)與靈魂拷問
三個必背知識點:
setInterval
誤差會累積,遞歸 setTimeout
才是王道- 定時器必須配合清除邏輯,否則內(nèi)存泄漏分分鐘
- 復(fù)雜任務(wù)請交給 Web Worker,別折磨主線程
轉(zhuǎn)自https://juejin.cn/post/7481909735869235263
該文章在 2025/3/18 9:37:54 編輯過