单纯把 setInterval 搬进 Worker 并不能提高精度,因 Worker 定时器同样受浏览器节流影响;真正有效的是主线程感知页面状态、Worker 构建可校准时钟:主线程监听 visibilitychange 发送 pause/resume 消息并同步本地时间,Worker 基于时间戳轮询、动态累加 pausedDuration 补偿挂起时长,推荐使用 worker-timers 库实现稳定 100ms 精度。

单纯把 setInterval 搬进 Worker 并不能提高精度——Worker 里的定时器同样受浏览器节流影响,后台时照样被压到 1 秒。真正有效的做法是:用主线程感知页面状态,用 Worker 构建可校准的时间逻辑,两者配合补上“挂起缺口”。
主线程负责状态感知与指令同步
Worker 无法读取 document.hidden,必须由主线程主动监听并通知:
- 监听
visibilitychange事件,页面隐藏时立即postMessage({ type: 'pause', timestamp: Date.now() }) - 页面恢复可见时,再发
{ type: 'resume', timestamp: Date.now() } - 每 30 秒左右向 Worker 同步一次本地时间,用于后续漂移校准
Worker 内构建时间戳驱动的逻辑时钟
放弃固定间隔的 setTimeout(fn, 100),改用基于时间戳的轮询判断:
PigX UI Pro 前端开发指南 - Vue 3 + TypeScript + Element Plus。当用户提到 PigX UI、PigX 前端、lgb-mgui 项目、Vue 3 企业级后台开发、Element Plus 后台开发时使用此技能。
- 初始化记录
baseTime = Date.now(),作为所有任务的起点 - 用短周期(如
setTimeout(..., 50))持续轮询当前真实时间 - 每次计算“本该触发的时刻”:
baseTime + interval * count + pausedDuration - 仅当
Date.now() >= 应触发时刻时才执行任务,并递增count
利用 pausedDuration 动态补偿挂起时间
关键不是让定时器“不停”,而是准确知道它“停了多久”:
- Worker 中维护一个累加变量
pausedDuration,初始为 0 - 每次收到
pause消息,记录暂停时间戳;收到resume时,累加本次挂起时长 - 所有任务触发判断都包含这个偏移量,确保恢复后节奏不跳、不堆积
优先使用 worker-timers 这类成熟封装库
手动实现易出错,推荐直接集成已验证方案:
- 安装:
npm install worker-timers - 导入后可无缝替换原生 API:
import { setInterval } from 'worker-timers' - 它自动处理 visibility 状态同步、暂停累计、时间校准、ID 管理,调用方式与原生一致
- 实测在后台标签页中仍能稳定维持 100ms 级精度,无跳变、无延迟堆积

















