可靠时钟应避免setInterval而用requestAnimationFrame+Date.now()或校准式setTimeout,因浏览器会节流非活跃标签页的定时器导致跳秒;核心是每次渲染时读取真实系统时间并计算。

直接用 setInterval 配合 new Date() 就能实现,但关键不是“能动”,而是“不卡、不漂、不崩”——尤其页面切到后台再切回来时,很多写法会跳秒甚至倒退。
为什么 setInterval(fn, 1000) 不可靠?
浏览器在标签页非活跃状态(比如切换到其他 tab 或最小化窗口)时,会节流甚至暂停 setInterval,导致计时器“醒来”后一次性执行多次回调,时间显示突变。这不是 bug,是规范行为。
- 实测:Chrome 对隐藏 tab 的
setInterval最低限制为 1000ms,但实际可能延迟 2–5 秒才触发 - 后果:时钟跳秒、秒针卡顿、
new Date().getSeconds()返回旧值 - 正确思路:不依赖定时器“准时”,而靠每次渲染时读取真实系统时间
用 requestAnimationFrame + Date.now() 做真实时更新
它每帧触发一次(通常 60fps),且在页面可见时稳定运行;配合 Date.now() 获取毫秒级时间戳,再手动计算时分秒,就能避开节流陷阱。
function updateClock() {
const now = Date.now();
const date = new Date(now);
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
document.getElementById('clock').textContent = `${hours}:${minutes}:${seconds}`;
requestAnimationFrame(updateClock);
}
updateClock();- 不用
setInterval,避免后台唤醒抖动 - 用
Date.now()而非new Date()构造函数调用(微小性能差异,但更语义清晰) -
padStart(2, '0')确保个位数补零,比date.getHours() % 12 || 12这类写法更直白可控
如果只要“每秒更新”,又想简单点?用 setTimeout 递归校准
比 setInterval 更可控:每次更新完,立刻根据当前时间决定下一次执行时机,把误差摊平。
立即学习“前端免费学习笔记(深入)”;
function tick() {
const now = new Date();
const msToNextSecond = 1000 - now.getMilliseconds();
setTimeout(() => {
const t = new Date();
document.getElementById('clock').textContent =
`${String(t.getHours()).padStart(2,'0')}:${String(t.getMinutes()).padStart(2,'0')}:${String(t.getSeconds()).padStart(2,'0')}`;
tick();
}, msToNextSecond);
}
tick();- 首次延迟由
getMilliseconds()决定,后续每次都在整秒边界触发 - 即使页面刚恢复可见,也能快速追上真实时间,不会跳秒
- 注意:别在
setTimeout外层再套setInterval,那又回到节流问题
真正难的不是写出能动的时钟,而是让它在用户切走微信、看邮件、锁屏再回来时,依然稳稳地走——这时候你得放弃“每秒执行一次”的执念,转而信任系统时间本身。



















