Skip to content
On this page

JavaScript 定时器校准

setIntervalsetTimeout 实现长定时场景时会导致延迟越来越大。

setInterval 导致延迟越来越大

简单示例:

js
let start = +new Date()
let count = 0

// 计时器
setInterval(() => {
  count++
  let delay = +new Date() - (start + count * 1000)
  console.log('【延迟毫秒数:】', delay)
}, 1000)

// 输出
// 【延迟毫秒数:】 2
// 【延迟毫秒数:】 2
// 【延迟毫秒数:】 7
// 【延迟毫秒数:】 8
// 【延迟毫秒数:】 11
// 【延迟毫秒数:】 16
// 【延迟毫秒数:】 17
// 【延迟毫秒数:】 17
// 【延迟毫秒数:】 22

setTimeout 导致延迟越来越大

简单示例:

js
let start = +new Date()
let count = 0
let handler = () => {
  count++
  let delay = +new Date() - (start + count * 1000)
  setTimeout(handler, 1000)
  console.log('【延迟毫秒数:】', delay)
}
setTimeout(handler, 1000)

// 输出
// 【延迟毫秒数:】 7
// 【延迟毫秒数:】 9
// 【延迟毫秒数:】 14
// 【延迟毫秒数:】 19
// 【延迟毫秒数:】 23
// 【延迟毫秒数:】 24
// 【延迟毫秒数:】 29
// 【延迟毫秒数:】 34
// 【延迟毫秒数:】 39

通过 setTimeout 校准时间间隔

  • 简单示例:
js
let start = +new Date()
let count = 0
let handler = () => {
  count++
  let delay = +new Date() - (start + count * 1000)
  let gap = 1000 - delay
  let nextTime = gap < 0 ? 0 : gap;
  setTimeout(handler, nextTime)
  console.log('【延迟毫秒数:】', delay)
}
setTimeout(handler, 1000)

// 输出
// 【延迟毫秒数:】 1
// 【延迟毫秒数:】 1
// 【延迟毫秒数:】 4
// 【延迟毫秒数:】 4
// 【延迟毫秒数:】 5
// 【延迟毫秒数:】 2
// 【延迟毫秒数:】 4
// 【延迟毫秒数:】 5
// 【延迟毫秒数:】 1
  • 实际应用:
js
/**
 * 设置精确的定时器
 */
function setAccurateInterval(func, wait = 0) {
  let start = +new Date()
  let count = 0
  let timer = null
  let interval = () => {
    count++
    let delay = +new Date() - (start + count * wait)
    let gap = wait - delay
    let nextTime = gap < 0 ? 0 : gap
    clearTimeout(timer)
    timer = setTimeout(interval, nextTime)

    typeof func === 'function' && func()
  }
  timer = setTimeout(interval, wait)

  return {
    cancel() {
      clearTimeout(timer)
    }
  }
}

// 使用
const interval = setAccurateInterval(() => {
  console.log(new Date())
}, 1000)
setTimeout(() => interval.cancel(), 10000)