更准的定时方案是每帧用 performance.now() 动态校正:raf+时间戳控单次延时(±1ms)、递归settimeout修正周期任务、postmessage实现微任务级调度、css动画保障视觉计时稳定。

浏览器里想比 setTimeout 更准,关键不是“等时间到了再执行”,而是“每帧/每次检查现在离目标还差多少”,用真实时间戳动态校正。原生定时器受事件循环、最小延迟(4ms)、后台降频(1000ms+)和执行耗时拖累,误差会累积;高精度方案绕开这些限制,靠主动测量+高频轮询或系统级时钟来对齐真实时间。
用 performance.now() + requestAnimationFrame 校准延时
适合动画类、毫秒级敏感的单次延时任务。利用 requestAnimationFrame 每帧触发的特性(约 16.6ms 一帧),配合高精度时间戳 performance.now() 实时比对,误差通常控制在 ±1ms 内。
- 不依赖 setTimeout 的排队机制,避免主线程阻塞带来的延迟
- 每一帧都计算当前已过时间,只在真正达标时才执行回调
- 代码轻量,兼容性好(IE10+ 支持 performance.now,RAF 全平台支持)
递归 setTimeout + 时间戳修正做周期任务
替代 setInterval,解决“执行堆积”和“时间漂移”问题。每次回调结束时,根据起始时间与目标间隔重新计算下一次触发时刻,而不是简单固定 delay。
- 例如:计划每 1000ms 执行一次,但某次执行耗时 120ms,则下次实际延迟为 880ms,保证整体节奏对齐真实时间
- 核心是记录
startTime和累计应触发次数,用performance.now() - startTime判断是否到达理论时刻 - 比 setInterval 更可控,不会因卡顿导致多个回调连发
用 postMessage 实现接近 0ms 延迟的微任务调度
绕过浏览器对 setTimeout 的 4ms 下限限制。利用 postMessage 触发 message 事件,其分发属于微任务队列,延迟极低(实测常低于 0.1ms)。
- 适用于需要极速响应的场景,比如 React 时间切片、高频状态同步
- 需自行维护任务队列,避免频繁 post 导致消息泛滥
- 注意跨域限制,调用时 targetOrigin 建议设为 "*"
CSS 动画驱动的纯前端计时(无 JS 干预)
对 UI 层计时(如倒计时进度条、时钟指针)最稳定的方式。由渲染引擎直接控制,不受 JS 主线程影响,即使页面卡死,动画仍可按帧率运行。
- 用
@keyframes定义 100% 对应 1s 的动画,绑定元素 transition 或 animation - 通过
animationend或getComputedStyle读取动画进度反推时间 - 缺点是无法触发复杂逻辑,适合视觉反馈类计时











