setTimeout 和 setInterval 指定延迟时间并不精确?如何更精确的控制延时?

777 阅读1分钟

关于 setTimeoutsetInterval 这两个函数都属于 Web API,与 JavaScript 核心语言特性并不直接相关。使用它们时需要注意,指定的延时时间并不是精确的,因为 JavaScript 是单线程的,如果主线程正在忙于执行其他任务,定时器的回调函数会被推迟执行。

那么要在 JavaScript 中更精确地控制延时,则可以使用 performance.now()requestAnimationFrame 结合实现。这种方法可以更精确的时间间隔执行某个任务,尤其是在动画场景中表现优秀。以下是一个例子:

function preciseTimeout(callback, delay) {
  const startTime = performance.now();

  function checkTime() {
    requestAnimationFrame(() => {
      const currentTime = performance.now();
      const elapsedTime = currentTime - startTime;

      if (elapsedTime >= delay) {
        callback();
      } else {
        checkTime();
      }
    });
  }

  checkTime();
}

// 使用示例
preciseTimeout(() => {
    console.log("确实好使");
}, 1000);

上述代码中,preciseTimeout 函数接收一个回调函数 callback 和一个延迟时间 delayperformance.now() 用于获取当前时间,requestAnimationFrame 用于在每一帧执行 checkTime 函数。当经过的时间大于或等于指定的延迟时间时,将执行回调函数。

需要注意的是,尽管requestAnimationFrame可以提供相对更精确的延时并与浏览器刷新率同步(通常为16.7ms,即60Hz屏幕刷新率),但它仍受到JavaScript单线程执行的限制。在主线程繁忙时,回调函数执行可能稍有延迟。相较之下,setTimeout的精度受限于浏览器的最小延时(通常为4ms)。因此,在实现动画或精确控制时间的场景中,requestAnimationFrame表现更优。