06setTimeout 与 setInterval 区别
基本概念
setTimeout
- 定义: 在指定的延迟时间后执行一次函数
- 语法:
setTimeout(callback, delay, ...args) - 返回值: 返回一个定时器 ID,可用于取消定时器
javascript
const timerId = setTimeout(() => {
console.log('执行一次');
}, 1000);
// 取消定时器
clearTimeout(timerId);setInterval
- 定义: 按照指定的周期(以毫秒计)重复调用函数
- 语法:
setInterval(callback, delay, ...args) - 返回值: 返回一个定时器 ID,可用于取消定时器
javascript
const intervalId = setInterval(() => {
console.log('重复执行');
}, 1000);
// 取消定时器
clearInterval(intervalId);核心区别
1. 执行次数
- setTimeout: 只执行一次
- setInterval: 重复执行,直到被清除
2. 执行时机
- setTimeout: 延迟指定时间后执行一次
- setInterval: 每隔指定时间重复执行
javascript
// setTimeout - 执行一次
setTimeout(() => {
console.log('1秒后执行一次');
}, 1000);
// setInterval - 每隔1秒执行
setInterval(() => {
console.log('每隔1秒执行一次');
}, 1000);3. 时间间隔的计算方式
setTimeout:
- 从上一次执行完成后开始计时
- 时间间隔 = 延迟时间 + 执行时间
setInterval:
- 从上一次执行开始时计时
- 时间间隔 = 延迟时间(不考虑执行时间)
javascript
// setTimeout 递归实现定时
function mySetTimeout() {
console.log('执行任务');
// 任务执行完后,再设置下一次定时
setTimeout(mySetTimeout, 1000);
}
setTimeout(mySetTimeout, 1000);
// setInterval 直接定时
setInterval(() => {
console.log('执行任务');
// 不管任务是否执行完,都会按固定间隔触发
}, 1000);重要问题
setInterval 的问题
1. 任务堆积问题
如果某次任务执行时间过长,可能导致多个任务堆积:
javascript
// 错误示例:任务执行时间超过间隔时间
setInterval(() => {
// 假设这个任务需要 2 秒
heavyTask(); // 耗时 2000ms
}, 1000); // 但间隔只有 1 秒
// 问题:任务还没执行完,下一个任务就被加入队列2. 无法保证执行间隔
setInterval 不会等待上一次任务完成,可能导致实际间隔不准确。
javascript
let count = 0;
setInterval(() => {
console.log('开始:', new Date().getTime());
// 模拟耗时操作
for(let i = 0; i < 1000000000; i++) {}
console.log('结束:', new Date().getTime());
count++;
if(count > 3) clearInterval(this);
}, 1000);
// 输出会发现实际间隔远大于 1000mssetTimeout 递归实现定时的优势
javascript
function myInterval() {
console.log('执行任务');
// 任务执行完成后,再设置下一次定时
setTimeout(() => {
myInterval(); // 递归调用
}, 1000);
}
// 启动
setTimeout(myInterval, 1000);优势:
- 确保每次任务执行完后,才开始计时
- 避免任务堆积
- 可以根据上一次执行结果动态调整间隔
javascript
// 动态调整间隔时间
function dynamicInterval(delay) {
console.log('执行任务');
setTimeout(() => {
// 根据条件调整下次执行的延迟时间
const nextDelay = someCondition ? 1000 : 2000;
dynamicInterval(nextDelay);
}, delay);
}
dynamicInterval(1000);实际应用场景
适合使用 setTimeout 的场景
- 延迟执行: 延迟显示提示信息
javascript
setTimeout(() => {
showNotification('操作成功');
}, 2000);- 防抖 (Debounce): 用户输入完成后再执行
javascript
function debounce(fn, delay) {
let timer = null;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}- 递归定时任务: 确保任务按序执行
javascript
function pollData() {
fetchData().then(data => {
processData(data);
// 处理完成后再设置下一次
setTimeout(pollData, 5000);
});
}适合使用 setInterval 的场景
- 固定频率更新: 时钟显示
javascript
setInterval(() => {
updateClock();
}, 1000);- 动画效果: 简单的帧动画(但建议用 requestAnimationFrame)
javascript
setInterval(() => {
moveElement();
}, 16); // 约 60fps- 轮询: 定时检查状态(简单场景)
javascript
const checkStatus = setInterval(() => {
if (isComplete()) {
clearInterval(checkStatus);
}
}, 1000);性能对比
javascript
// 测试 setInterval
console.time('setInterval');
let count1 = 0;
const interval = setInterval(() => {
count1++;
if (count1 === 10) {
clearInterval(interval);
console.timeEnd('setInterval');
}
}, 100);
// 测试 setTimeout 递归
console.time('setTimeout');
let count2 = 0;
function recursiveTimeout() {
count2++;
if (count2 === 10) {
console.timeEnd('setTimeout');
return;
}
setTimeout(recursiveTimeout, 100);
}
setTimeout(recursiveTimeout, 100);注意事项
1. 最小延迟时间
浏览器对定时器有最小延迟限制:
- HTML5 规范要求最小 4ms
- 嵌套层级超过 5 层后,最小间隔为 4ms
- 未激活的标签页可能延长到 1000ms
javascript
// 延迟可能不是真的 0ms
setTimeout(() => {
console.log('延迟执行');
}, 0); // 实际延迟约 4ms2. this 指向问题
javascript
const obj = {
name: '对象',
method() {
// 错误:this 指向 window
setTimeout(function() {
console.log(this.name); // undefined
}, 1000);
// 正确:使用箭头函数
setTimeout(() => {
console.log(this.name); // '对象'
}, 1000);
}
};3. 内存泄漏风险
javascript
// 错误:忘记清除定时器
const interval = setInterval(() => {
// 一些操作
}, 1000);
// 正确:在组件卸载时清除
// Vue 示例
export default {
data() {
return {
timer: null
}
},
mounted() {
this.timer = setInterval(() => {
// 操作
}, 1000);
},
beforeUnmount() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
}
}总结
| 特性 | setTimeout | setInterval |
|---|---|---|
| 执行次数 | 一次 | 重复执行 |
| 时间计算 | 从执行完成后开始计时 | 从执行开始时计时 |
| 堆积问题 | 无(递归使用时) | 可能产生 |
| 灵活性 | 高(可动态调整) | 低(固定间隔) |
| 使用场景 | 延迟执行、防抖节流 | 固定频率更新、轮询 |
| 推荐程度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
最佳实践:
- 对于需要重复执行的任务,优先考虑使用 setTimeout 递归实现
- setInterval 适用于简单的固定频率任务
- 记得在适当的时候清除定时器,避免内存泄漏
- 对于动画,优先使用
requestAnimationFrame