一、计时事件核心API
JavaScript提供了两种主要的计时方法:
// 延时执行
const timeoutId = setTimeout(callback, delay, arg1, arg2);
// 间隔执行
const intervalId = setInterval(callback, delay, arg1, arg2);
// 清除计时器
clearTimeout(timeoutId);
clearInterval(intervalId);
二、事件循环机制揭秘
JavaScript是单线程语言,计时事件并非精确到点执行,而是等待主线程空闲后才会执行。这意味着设置的延迟时间只是最小等待时间,而非确切执行时间。
console.log('开始');
setTimeout(() => {
console.log('延时执行');
}, 0);
console.log('结束');
// 输出顺序:开始 → 结束 → 延时执行
三、常见陷阱与解决方案
1. 闭包内存泄漏
// 错误示例
function createTimer() {
const data = new Array(1000000).fill('大数据');
setInterval(() => {
console.log(data.length); // 保持对data的引用
}, 1000);
}
// 正确做法
function createTimer() {
const data = new Array(1000000).fill('大数据');
const id = setInterval(() => {
// 使用完后清除引用
if(needClear) {
clearInterval(id);
}
}, 1000);
}
2. 误差累积问题
// 使用递归setTimeout替代setInterval
function repeat() {
setTimeout(() => {
doTask();
repeat(); // 递归调用
}, 1000);
}
// 精确计时器
class AccurateTimer {
constructor(callback, interval) {
this.expected = Date.now() + interval;
this.timeout = null;
this.interval = interval;
this.callback = callback;
this.step();
}
step() {
const drift = Date.now() - this.expected;
this.callback();
this.expected += this.interval;
this.timeout = setTimeout(() => this.step(), Math.max(0, this.interval - drift));
}
stop() {
clearTimeout(this.timeout);
}
}
四、实战示例:动画控制器
class AnimationController {
constructor() {
this.animationId = null;
this.startTime = null;
this.duration = 2000; // 动画持续时间
}
animate(timestamp) {
if (!this.startTime) this.startTime = timestamp;
const elapsed = timestamp - this.startTime;
const progress = Math.min(elapsed / this.duration, 1);
// 更新动画状态
this.update(progress);
if (progress < 1) {
this.animationId = requestAnimationFrame(this.animate.bind(this));
} else {
this.complete();
}
}
start() {
this.animationId = requestAnimationFrame(this.animate.bind(this));
}
stop() {
cancelAnimationFrame(this.animationId);
}
update(progress) {
// 实际动画更新逻辑
element.style.opacity = progress;
}
complete() {
console.log('动画完成');
}
}
五、浏览器后台运行限制
现代浏览器对后台标签页的计时器执行频率有限制(通常降低到1秒/次)。使用Web Worker可以避免此限制:
// 在主线程中
const worker = new Worker('timer-worker.js');
worker.postMessage({ delay: 1000 });
// 在timer-worker.js中
self.addEventListener('message', (e) => {
const delay = e.data.delay;
setInterval(() => {
self.postMessage('tick');
}, delay);
});
总结
JavaScript计时事件是前端开发的核心功能,理解其底层机制和潜在陷阱至关重要。掌握requestAnimationFrame、Web Worker等现代API,结合本文介绍的实践技巧,将帮助你构建更加可靠、高效的异步应用。

被折叠的 条评论
为什么被折叠?



