前端必会:如何创建一个可随时取消的定时器
一、原生的取消方式
JavaScript 原生就提供了取消定时器的方法。setTimeout
和 setInterval
在调用时都会返回一个数字类型的 ID,我们可以将这个 ID 传递给 clearTimeout
或 clearInterval
来取消它。
// 1. 设置一个定时器
const timerId: number = setTimeout(() => {
console.log("这个消息可能永远不会被打印");
}, 2000);
// 2. 在它触发前取消它
clearTimeout(timerId);
常见痛点:
-
timerId
变量需要被保留在组件或模块的作用域中,状态分散。 - 启动、暂停、取消的逻辑是割裂的,代码可读性和可维护性差。
二、封装一个可取消的定时器类
我们可以简单的封装一个 CancellableTimer
类,将定时器的状态和行为内聚在一起。后续可以扩展,把项目中的所有定时器进行统一管理。
// 定义定时器ID类型
type TimeoutId = ReturnType<typeof setTimeout>;
class CancellableTimer {
private timerId: TimeoutId | null = null;
constructor(private callback: () => void, private delay: number) {}
public start(): void {
// 防止重复启动
if (this.timerId !== null) {
this.cancel();
}
this.timerId = setTimeout(() => {
this.callback();
// 执行完毕后重置 timerId
this.timerId = null;
}, this.delay);
}
public cancel(): void {
if (this.timerId !== null) {
clearTimeout(this.timerId);
this.timerId = null;
}
}
}
// 使用示例
console.log('定时器将在3秒后触发...');
const myTimer = new CancellableTimer(() => {
console.log('定时器任务执行!');
}, 3000);
myTimer.start();
// 模拟在1秒后取消
setTimeout(() => {
console.log('用户取消了定时器。');
myTimer.cancel();
}, 1000);
三、实现可暂停和恢复的定时器
在很多场景下,我们需要的不仅仅是取消,还有暂停和恢复。
要实现这个功能,我们需要在暂停时记录剩余时间。
type TimeoutId = ReturnType<typeof setTimeout>;
class AdvancedTimer {
private timerId: TimeoutId | null = null;
private startTime: number = 0;
private remainingTime: number;
private callback: () => void;
private delay: number;
constructor(callback: () => void, delay: number) {
this.remainingTime = delay;
this.callback = callback;
this.delay = delay;
}
public resume(): void {
if (this.timerId) {
return; // 已经在运行
}
this.startTime = Date.now();
this.timerId = setTimeout(() => {
this.callback();
// 任务完成,重置
this.remainingTime = this.delay;
this.timerId = null;
}, this.remainingTime);
}
public pause(): void {
if (!this.timerId) {
return;
}
clearTimeout(this.timerId);
this.timerId = null;
// 计算并更新剩余时间
const timePassed = Date.now() - this.startTime;
this.remainingTime -= timePassed;
}
public cancel(): void {
if (this.timerId) {
clearTimeout(this.timerId);
}
this.timerId = null;
this.remainingTime = this.delay; // 重置
}
}
// 使用示例
console.log('定时器启动,5秒后执行...');
const advancedTimer = new AdvancedTimer(() => console.log('Done!'), 5000);
advancedTimer.resume();
setTimeout(() => {
console.log('2秒后暂停定时器');
advancedTimer.pause();
}, 2000);
setTimeout(() => {
console.log('4秒后恢复定时器 , 应该还剩3秒');
advancedTimer.resume();
}, 4000);
总结
如果你喜欢本教程,记得点赞+收藏!关注我获取更多JavaScript/TypeScript开发干货