Appearance
2805. 自定义间隔
ts
type IntervalFn = () => void
interface CustomIntervalMap {
[id: number]: boolean
}
// 用于存储每个定时器的"取消"状态
const intervalMap: CustomIntervalMap = {}
let uid = 0
/**
* 模拟 setInterval,支持 clear
* @param fn 执行的函数
* @param delay 首次延迟
* @param period 周期间隔
* @returns id 可用于 clear
*/
function customInterval(fn: IntervalFn, delay: number, period: number): number {
const id = ++uid
intervalMap[id] = false
let count = 0
const cb = () => {
if (intervalMap[id]) return // 被 clear 掉
fn()
setTimeout(cb, count++ * period + delay)
}
setTimeout(cb, count++ * period + delay)
return id
}
/**
* 清除定时器
* @param id 定时器 id
*/
function customClearInterval(id: number): void {
intervalMap[id] = true
}与 setInterval 的关键差异
原生 setInterval 的间隔是固定的,不管回调执行多久。如果回调耗时超过间隔,会导致回调堆积。
这里用 setTimeout 链式调用来模拟 interval:每次回调执行完后,再设置下一次定时器。这保证了两次回调执行之间至少间隔 period,不会出现连续堆积。
取消机制
因为链式 setTimeout 没有像 clearInterval 那样直接对应的统一清理入口,这里用了一个外部 intervalMap 标记取消状态。下一次回调执行前先检查标记,如果已取消就直接返回,不再继续设置后续定时器。
count 变量的递增用于累加间隔时间——首次 delay,之后每次 period。
一个注意点
如果 fn 抛出异常,setTimeout(cb, ...) 就不会继续调用,整个链条会直接中断。实际场景里通常要再补一层 try/catch,决定是继续调度还是上报后终止。
