一、基础语法与核心差异
1. setTimeout
:单次延迟执行
javascript
const timerId = setTimeout(callback, delay, arg1, arg2, ...);
• 功能 :在指定延迟(毫秒)后执行一次回调函数。
• 取消方法 :clearTimeout(timerId)
。
2. setInterval
:循环重复执行
javascript
const intervalId = setInterval(callback, interval, arg1, arg2, ...);
• 功能 :每隔固定时间重复执行回调函数。
• 取消方法 :clearInterval(intervalId)
。
核心差异 :
• setTimeout
:单次触发 ,适合延迟任务或递归实现循环。
• setInterval
:固定间隔触发,适合周期性任务(需注意累积问题)。
二、实战技巧与经典问题
1. 用setTimeout
模拟setInterval
由于setInterval
可能因回调执行时间过长导致任务堆积 ,更安全的做法是递归调用setTimeout
:
javascript
function safeInterval(callback, interval) {
let timerId = null;
function execute() {
callback();
timerId = setTimeout(execute, interval); // 递归调用
}
timerId = setTimeout(execute, interval);
return () => clearTimeout(timerId); // 返回清除函数
}
此方法确保每次回调执行完毕后再重新计时,避免任务重叠。
2. 事件循环与时间精度问题
JavaScript是单线程的,定时器的回调会被推入任务队列,实际执行时间可能晚于预期:
javascript
console.log("Start");
setTimeout(() => console.log("Timeout"), 0);
console.log("End");
// 输出顺序:Start → End → Timeout
即使延迟设为0,回调仍需等待主线程空闲。
3. 内存泄漏:忘记清除定时器
未及时调用clearTimeout
或clearInterval
会导致回调持续引用外部变量,内存无法释放:
javascript
function startPolling() {
setInterval(() => {
fetchData(); // 持续运行,即使组件已销毁
}, 5000);
}
解决方案:在组件卸载或条件变更时主动清除定时器。
三、高级应用场景
1. 节流(Throttling)与防抖(Debouncing)
• 防抖:连续事件中只执行最后一次:
javascript
function debounce(func, delay) {
let timerId;
return (...args) => {
clearTimeout(timerId);
timerId = setTimeout(() => func(...args), delay);
};
}
• 节流:固定时间内只执行一次:
javascript
function throttle(func, interval) {
let lastTime = 0;
return (...args) => {
const now = Date.now();
if (now - lastTime >= interval) {
func(...args);
lastTime = now;
}
};
}