-
打个分吧:

时钟准确的定时轮询函数

js的单线程异步机制决定了setInterval和setTimeout函数的时钟不准确性

2分钟阅读
-
-

浏览器原生的setTimeout(fn,timeout)函数的原理,是保证timeout时间后,将回调函数fn加入event loop的消息队列,不是保证timeout时间后一定执行。

回调函数加入的消息队列后,实际的执行时间要等到 调用栈中已有函数 + 消息队列已有消息执行完毕。

所以回调函数的实际执行时间与设定的timeout有偏差。

setInterval函数也同样有偏差。

以下实现了一个时间准确的定时轮询函数。

setClockInterval 函数

/**
 * setClockInterval是时钟准确的定时轮询函数(原生setInterval时钟不准确)
 * @param func 与原生setInterval参数一致
 * @param interval 与原生setInterval参数一致
 * @return timer 类型为number
 */
const timerMap = new Map()
export function setClockInterval(func: Function, interval: number) {
  let start: number
  let tick: number
  let clockTimer: number | null
  const timerId = Math.floor(Math.random() * 1e10)
  const recurFunc = () => {
    func()
    const realExcuTime = new Date().getTime()
    if (!start) {
      start = realExcuTime
    }
    tick = tick || start

    // bias是本次实际执行时间戳 与 理论执行时间戳tick之间的偏差
    // bias一定>=0,因为浏览器的setTimeout setInterval只会准时或者延迟执行,不会提前执行
    // 经过本地浏览器统计 bias平均值在10ms以内,最大值在20ms左右
    const bias = realExcuTime - tick
    console.log(`实际执行时间戳${realExcuTime},理论计划执行时间戳${tick},本次偏差${bias}ms`);

    // 下次tick时间戳
    tick += interval

    // 因为本次实际执行时间有bias的延迟,所以下次执行提前
    clockTimer = window.setTimeout(recurFunc, interval - bias)
    timerMap.set(timerId, clockTimer)
  }
  recurFunc()
  return timerId
}

clearClockInterval 函数

/**
 * clearClockInterval 用于终止 setClockInterval 的定时轮询
 * @param timer 是 setClockInterval 的返回值
 */
export function clearClockInterval(timerId: number) {
  if (timerMap.has(timerId)) {
    window.clearTimeout(timerMap.get(timerId))
  } else {
    console.warn(`timerId${timerId}not found`);
  }
}

demo执行效果

bias平均值在10ms以内,最大值在20ms左右

09:45:39.557 util.ts:23 实际执行时间戳1690508739557,理论计划执行时间戳1690508739550,本次偏差7ms
09:45:40.555 util.ts:23 实际执行时间戳1690508740555,理论计划执行时间戳1690508740550,本次偏差5ms
09:45:41.561 util.ts:23 实际执行时间戳1690508741561,理论计划执行时间戳1690508741550,本次偏差11ms
09:45:42.559 util.ts:23 实际执行时间戳1690508742559,理论计划执行时间戳1690508742550,本次偏差9ms
09:45:43.559 util.ts:23 实际执行时间戳1690508743559,理论计划执行时间戳1690508743550,本次偏差9ms
09:45:44.560 util.ts:23 实际执行时间戳1690508744560,理论计划执行时间戳1690508744550,本次偏差10ms
09:45:45.556 util.ts:23 实际执行时间戳1690508745555,理论计划执行时间戳1690508745550,本次偏差5ms
09:45:46.552 util.ts:23 实际执行时间戳1690508746552,理论计划执行时间戳1690508746550,本次偏差2ms
09:45:47.562 util.ts:23 实际执行时间戳1690508747562,理论计划执行时间戳1690508747550,本次偏差12ms
09:45:48.561 util.ts:23 实际执行时间戳1690508748561,理论计划执行时间戳1690508748550,本次偏差11ms
上次更新:

评论区