JavaScript 定时器详解
一、两种定时器的区别
1.
setTimeout()
// 语法:setTimeout(回调函数, 延迟时间, 参数1, 参数2...)
// 只执行一次,延迟指定时间后执行
const timer1 = setTimeout(() => {
console.log('3秒后执行一次');
}, 3000);
2.
setInterval()
// 语法:setInterval(回调函数, 间隔时间, 参数1, 参数2...)
// 每隔指定时间重复执行
const timer2 = setInterval(() => {
console.log('每隔2秒执行一次');
count++;
}, 2000);
主要区别对比表:
| 特性 |
setTimeout |
setInterval |
|---|
| 执行次数 |
只执行一次 |
重复执行 |
| 延迟方式 |
延迟指定时间后执行 |
每隔指定时间执行 |
| 应用场景 |
延迟任务、防抖节流 |
轮询、动画、计时器 |
| 时间精度 |
可能因事件循环延迟 |
可能累积误差 |
二、清除定时器的两种方法
1.
clearTimeout() - 清除setTimeout
let timer = setTimeout(() => {
console.log('这个不会执行');
}, 5000);
// 在5秒前清除
clearTimeout(timer);
console.log('定时器已清除');
2.
clearInterval() - 清除setInterval
let count = 0;
let intervalTimer = setInterval(() => {
count++;
console.log(`执行次数: ${count}`);
// 执行5次后清除
if (count >= 5) {
clearInterval(intervalTimer);
console.log('定时器已停止');
}
}, 1000);
三、详细示例与注意事项
示例1:两种定时器的基本使用
// setTimeout 示例
function showMessage(message) {
console.log(`消息: ${message}`);
}
// 3秒后执行,并传递参数
const timeoutId = setTimeout(showMessage, 3000, 'Hello World');
// setInterval 示例
let seconds = 0;
const intervalId = setInterval(() => {
seconds++;
console.log(`已过去 ${seconds} 秒`);
if (seconds >= 5) {
clearInterval(intervalId);
console.log('计时结束');
}
}, 1000);
示例2:在实际项目中的应用
// 轮询API数据
let pollTimer = null;
function pollData() {
pollTimer = setInterval(async () => {
try {
const response = await fetch('/api/data');
const data = await response.json();
updateUI(data);
} catch (error) {
console.error('轮询失败:', error);
clearInterval(pollTimer);
}
}, 5000);
}
// 防抖函数中使用
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
四、注意事项和最佳实践
1.
定时器ID的处理
// 正确的清理方式
let timerIds = [];
// 存储所有定时器ID
timerIds.push(setTimeout(() => {}, 1000));
timerIds.push(setInterval(() => {}, 2000));
// 批量清理
function clearAllTimers() {
timerIds.forEach(id => {
clearTimeout(id); // clearTimeout也能清除setInterval
clearInterval(id);
});
timerIds = [];
}
// 组件卸载时清理(Vue/React示例)
// Vue
beforeUnmount() {
clearTimeout(this.timer);
clearInterval(this.interval);
}
// React
useEffect(() => {
const timer = setInterval(() => {}, 1000);
return () => {
clearInterval(timer); // 清理函数
};
}, []);
2.
性能问题与替代方案
// 使用 requestAnimationFrame 替代 setInterval 做动画
function animate() {
// 动画逻辑
element.style.left = `${position}px`;
if (position < 500) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
// 避免定时器嵌套过深
setTimeout(function task() {
// 执行任务
if (condition) {
setTimeout(task, 100); // 使用嵌套而非setInterval
}
}, 100);
3.
常见问题解决
// 问题1:setInterval的累积执行问题
function accurateInterval(callback, interval) {
let expected = Date.now() + interval;
const timeout = setTimeout(tick, interval);
function tick() {
const drift = Date.now() - expected;
callback();
expected += interval;
setTimeout(tick, Math.max(0, interval - drift));
}
return {
clear: () => clearTimeout(timeout)
};
}
// 问题2:页面不可见时暂停定时器
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
// 暂停所有定时器
} else {
// 恢复定时器
}
});
五、实际应用场景
场景1:倒计时组件
class Countdown {
constructor(duration, onUpdate, onEnd) {
this.duration = duration;
this.onUpdate = onUpdate;
this.onEnd = onEnd;
this.timer = null;
this.remaining = duration;
}
start() {
this.timer = setInterval(() => {
this.remaining -= 1000;
this.onUpdate(this.remaining);
if (this.remaining <= 0) {
this.stop();
this.onEnd();
}
}, 1000);
}
stop() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
}
reset() {
this.stop();
this.remaining = this.duration;
}
}
场景2:自动保存功能
class AutoSaver {
constructor(saveFunction, interval = 30000) {
this.saveFunction = saveFunction;
this.interval = interval;
this.timer = null;
this.isDirty = false;
}
markDirty() {
this.isDirty = true;
// 防抖:用户连续输入时,延迟保存
if (this.timer) {
clearTimeout(this.timer);
}
this.timer = setTimeout(() => {
if (this.isDirty) {
this.save();
}
}, 1000);
}
startAutoSave() {
// 定期保存
this.autoSaveTimer = setInterval(() => {
if (this.isDirty) {
this.save();
}
}, this.interval);
}
save() {
this.saveFunction();
this.isDirty = false;
}
destroy() {
clearTimeout(this.timer);
clearInterval(this.autoSaveTimer);
}
}
总结要点
setTimeout 用于延迟执行,
setInterval 用于重复执行
clearTimeout 和
clearInterval 用于清除对应的定时器
实际上,clearTimeout 可以清除 setInterval,反之亦然(但不推荐混用)
定时器ID是数字,可以使用同一个函数清除
定时器的最小延迟时间是4ms(HTML5规范规定)
定时器在页面不可见时可能会被节流(浏览器优化)
推荐在组件销毁/页面卸载时清理所有定时器
记住:定时器如果不及时清理,可能导致内存泄漏!