时间:2025-08-30 17:30
人气:
作者:admin
点赞 + 收藏 === 学会????????????
JavaScript是单线程语言,所有任务(包括定时器回调)都在同一个线程中排队执行。当主线程被耗时任务(如复杂计算、网络请求)阻塞时,定时器回调只能“望队兴叹”,导致实际执行时间远晚于预期时间。就像一家只有一个收银台的超市,即使定时器提醒“该收银了”,但前面排队的顾客(同步任务)太多,收银员(主线程)根本腾不出手。
案例演示:
// 模拟主线程阻塞
let count = 0;
setInterval(() => {
console.log(`理论执行时间: ${count++}秒`);
// 阻塞主线程1.5秒
const start = Date.now();
while (Date.now() - start < 1500) {}
}, 1000);
运行结果:每次回调实际间隔2.5秒,误差高达150%!
当页面处于后台或设备锁屏时,浏览器会降低定时器执行频率(如Chrome将间隔延长至1秒以上),甚至暂停定时器以节省资源。这就像让倒计时在用户看不见时“偷懒睡觉”,导致重新激活页面时时间已大幅偏差。
用户可能手动修改设备时间,或设备未开启网络时间同步,导致本地时间与真实时间存在偏差。此时,基于Date.now()的倒计时会完全失去参考价值。
setTimeout核心思想:每次执行回调时,计算实际偏差(offset),动态调整下一次定时器的间隔时间。
代码实现:
function preciseCountdown(duration) {
let startTime = Date.now();
let expected = duration;
function step() {
const now = Date.now();
const elapsed = now - startTime;
const remaining = duration - elapsed;
if (remaining <= 0) {
console.log("倒计时结束");
return;
}
// 计算偏差并调整下一次执行时间
const drift = elapsed - expected;
expected += 1000;
const nextInterval = 1000 - drift;
console.log(`剩余时间: ${Math.round(remaining/1000)}秒,偏差: ${drift}ms`);
setTimeout(step, Math.max(0, nextInterval));
}
setTimeout(step, 1000);
}
效果:误差可控制在±50ms以内,适用于对精度要求较高的短时倒计时。
实现步骤:
serverTime。clientTime,计算差值delta = serverTime - clientTime。Date.now() + delta作为“真实时间”。通过visibilitychange事件检测页面是否可见,不可见时暂停计时,可见时重新校准时间。
实现代码:
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
// 记录暂停时间点
pauseTime = Date.now();
} else {
// 计算暂停期间流逝的时间并补偿
const resumeTime = Date.now();
elapsed += resumeTime - pauseTime;
}
});
将倒计时逻辑放在Web Worker线程中执行,避免主线程阻塞导致的误差。
Worker脚本示例:
// worker.js
let timer;
self.onmessage = (e) => {
if (e.data.command === 'start') {
const duration = e.data.duration;
const startTime = Date.now();
function step() {
const elapsed = Date.now() - startTime;
const remaining = duration - elapsed;
if (remaining <= 0) {
self.postMessage({ status: 'finished' });
return;
}
self.postMessage({ remaining });
timer = setTimeout(step, 1000 - (elapsed % 1000));
}
step();
} else if (e.data.command === 'stop') {
clearTimeout(timer);
}
};
performance.now()相比Date.now(),performance.now()提供微秒级精度且不受系统时间调整影响。
优势对比:

利用CSS动画的硬件加速特性渲染倒计时,JavaScript仅负责逻辑校准。
创新方案:
.countdown {
animation: countdown 10s linear;
animation-play-state: running;
}
@keyframes countdown {
from { --progress: 100%; }
to { --progress: 0%; }
}
// 监听动画每一帧
element.addEventListener('animationiteration', () => {
updateDisplay();
});
setTimeout修正 + performance.now()// 记录每次偏差用于分析
const driftHistory = [];
function logDrift(drift) {
driftHistory.push(drift);
if (drift > 100) {
console.warn('过大偏差警告:', drift);
}
}
requestAnimationFrame实现流畅渲染:function updateMilliseconds() {
const ms = remaining % 1000;
element.textContent = ms.toString().padStart(3, '0');
requestAnimationFrame(updateMilliseconds);
}
定时器延迟
setTimeout 和 setInterval 受主线程阻塞的影响,导致执行时机可能会有延迟。requestAnimationFrame 替代 setInterval 或 setTimeout,尤其是需要精确渲染的场景。或者使用 Web Workers 来在后台执行任务,不受主线程阻塞。JavaScript 单线程问题
设备与系统时钟差异
performance.now() 获取高精度时间。对于长时间运行的应用,定期同步时钟以减小误差。浏览器渲染与执行周期
requestAnimationFrame 来确保倒计时更新与页面渲染同步。此外,尽量避免阻塞渲染的操作,提高页面渲染的流畅性。