setinterval 不保证准时执行,因受主线程阻塞、浏览器实现差异、累积漂移及错误不中断等影响,易导致跳帧、降频、时间偏差和隐蔽异常。

因为 setInterval 的执行不依赖“绝对准时”,而是受制于 JavaScript 单线程运行机制和事件循环的调度规则,不同环境下的主线程负载、任务队列状态、浏览器实现细节甚至系统时钟精度都会直接影响它的实际行为。
主线程阻塞导致回调跳过或堆积
setInterval 每隔固定时间向任务队列投递一次回调,但若主线程正忙(比如执行长循环、大量 DOM 操作或同步计算),该回调只能排队等待。一旦排队时间超过下一个周期,浏览器通常不会补发被错过的调用,而是直接执行下一轮——造成“跳帧”。严重时多个回调积压,解封后集中爆发,看起来像突然连跑几次。
- 例如:设间隔 100ms,但某次回调耗时 300ms,后续三次本该触发的回调会被跳过,第 4 次才执行
- 这种跳过是浏览器主动丢弃,不是延迟累积;而 setTimeout 不会跳过,只会在队列中靠后排队
事件循环差异影响定时器唤醒时机
不同浏览器(Chrome/Firefox/Safari/鸿蒙 ArkTS)对宏任务队列的轮询频率、空闲检测策略、甚至定时器底层实现(如基于系统高精度定时器还是系统 tick)都有区别。部分浏览器在页面非活跃状态下还会主动降频 setInterval(如最小间隔拉长到 1s),而另一些则保持名义间隔但实际无法唤醒。
- 桌面 Chrome 在后台标签页中可能将 setInterval 限频至 1s 以上
- 微信小程序 WebView 或鸿蒙轻应用环境,定时器精度普遍低于桌面浏览器
- 某些旧版 IE 对传参处理不一致,导致回调函数接收参数异常
误差随运行时间持续放大
setInterval 的间隔是“理想间隔”,每次触发都以**上一次入队时间为基准**计算下一次投递时间,而非以实际执行时间为基准。如果某次回调执行耗时较长,后续所有触发点都会整体后移,形成累积漂移。
- 运行 10 分钟后,理论应触发 600 次(按 1s 间隔),实际可能只触发 580 次左右
- 倒计时类场景中,用户看到的剩余时间会比真实服务器时间慢数秒甚至十几秒
- 这种漂移无法靠重置 clearInterval + setInterval 消除,必须从时间锚点重新校准
错误不中断执行是隐蔽风险源
setInterval 完全无视回调内部是否抛错。哪怕每次执行都 throw Error,它仍会照常发起下一轮定时,错误仅向上冒泡至 window.onerror,极易掩盖逻辑缺陷。
- 网络请求失败未 catch,会导致控制台报错但定时器照跑,请求不断重发
- DOM 元素已销毁却还在操作,报 “Cannot read property 'xxx' of null” 后继续循环
- 相比而言,用 setTimeout 递归调用可在每次执行前做状态检查,天然具备容错入口











