setTimeout与setInterval实现
手写实现浏览器定时器API setTimeout和setInterval的功能
问题
在JavaScript中,setTimeout 和 setInterval 是常用的定时器API。这道题要求我们手动实现这两个函数的功能,包括:
- 延迟执行回调函数
- 支持传递参数
- 返回定时器ID用于清除
- 实现clearTimeout和clearInterval
解答
// 定时器管理类
class TimerManager {
constructor() {
this.timerId = 0; // 定时器ID计数器
this.timers = new Map(); // 存储所有定时器
}
/**
* 实现setTimeout
* @param {Function} callback - 回调函数
* @param {number} delay - 延迟时间(毫秒)
* @param {...any} args - 传递给回调函数的参数
* @returns {number} 定时器ID
*/
setTimeout(callback, delay = 0, ...args) {
const id = this.timerId++;
const timer = {
id,
type: 'timeout',
startTime: Date.now(),
callback,
delay,
args
};
// 使用原生setTimeout实现
const timeoutId = globalThis.setTimeout(() => {
// 执行回调
callback(...args);
// 执行完后删除定时器记录
this.timers.delete(id);
}, delay);
timer.nativeId = timeoutId;
this.timers.set(id, timer);
return id;
}
/**
* 实现setInterval
* @param {Function} callback - 回调函数
* @param {number} interval - 间隔时间(毫秒)
* @param {...any} args - 传递给回调函数的参数
* @returns {number} 定时器ID
*/
setInterval(callback, interval = 0, ...args) {
const id = this.timerId++;
const timer = {
id,
type: 'interval',
startTime: Date.now(),
callback,
interval,
args
};
// 使用原生setInterval实现
const intervalId = globalThis.setInterval(() => {
callback(...args);
}, interval);
timer.nativeId = intervalId;
this.timers.set(id, timer);
return id;
}
/**
* 清除setTimeout
* @param {number} id - 定时器ID
*/
clearTimeout(id) {
const timer = this.timers.get(id);
if (timer && timer.type === 'timeout') {
globalThis.clearTimeout(timer.nativeId);
this.timers.delete(id);
}
}
/**
* 清除setInterval
* @param {number} id - 定时器ID
*/
clearInterval(id) {
const timer = this.timers.get(id);
if (timer && timer.type === 'interval') {
globalThis.clearInterval(timer.nativeId);
this.timers.delete(id);
}
}
/**
* 清除所有定时器
*/
clearAll() {
this.timers.forEach(timer => {
if (timer.type === 'timeout') {
globalThis.clearTimeout(timer.nativeId);
} else {
globalThis.clearInterval(timer.nativeId);
}
});
this.timers.clear();
}
}
// 创建全局实例
const timerManager = new TimerManager();
// 导出API
const mySetTimeout = timerManager.setTimeout.bind(timerManager);
const mySetInterval = timerManager.setInterval.bind(timerManager);
const myClearTimeout = timerManager.clearTimeout.bind(timerManager);
const myClearInterval = timerManager.clearInterval.bind(timerManager);
使用示例
// 示例1: 基本的setTimeout使用
console.log('开始');
const timeoutId = mySetTimeout(() => {
console.log('1秒后执行');
}, 1000);
// 示例2: setTimeout传递参数
mySetTimeout((name, age) => {
console.log(`姓名: ${name}, 年龄: ${age}`);
}, 1500, '张三', 25);
// 示例3: 清除setTimeout
const id = mySetTimeout(() => {
console.log('这条不会执行');
}, 2000);
myClearTimeout(id);
// 示例4: 基本的setInterval使用
let count = 0;
const intervalId = mySetInterval(() => {
count++;
console.log(`执行次数: ${count}`);
// 执行5次后清除
if (count >= 5) {
myClearInterval(intervalId);
console.log('定时器已清除');
}
}, 500);
// 示例5: setInterval传递参数
const countdownId = mySetInterval((from) => {
console.log(`倒计时: ${from}`);
from--;
if (from < 0) {
myClearInterval(countdownId);
}
}, 1000, 5);
// 示例6: 清除所有定时器
mySetTimeout(() => console.log('定时器1'), 3000);
mySetTimeout(() => console.log('定时器2'), 4000);
mySetInterval(() => console.log('循环定时器'), 1000);
// 2秒后清除所有定时器
setTimeout(() => {
timerManager.clearAll();
console.log('所有定时器已清除');
}, 2000);
关键点
-
定时器ID管理:使用递增的ID来唯一标识每个定时器,便于后续清除操作
-
Map数据结构:使用Map存储定时器信息,支持快速查找和删除操作
-
参数传递:使用剩余参数(…args)和展开运算符支持向回调函数传递任意数量的参数
-
类型区分:通过type字段区分timeout和interval类型,确保清除时调用正确的原生API
-
自动清理:setTimeout执行完毕后自动从Map中删除,避免内存泄漏
-
绑定this:导出API时使用bind绑定timerManager实例,确保方法内this指向正确
-
扩展性:通过TimerManager类封装,便于添加更多功能(如暂停、恢复、查询等)
-
原生API封装:基于原生setTimeout/setInterval实现,保证了时间精度和性能
目录