Skip to content

2805. 自定义间隔

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,决定是继续调度还是上报后终止。