Skip to content

定时器

浏览器提供的定时器能力主要包括 setTimeoutsetInterval。它们不是 JavaScript 语言本身的语法,而是宿主环境提供的 Web API。

定时器解决什么问题

定时器的核心用途是把一段代码安排到“稍后”执行。

常见场景包括:

  • 延迟执行某个动作
  • 周期轮询
  • 节流动画或状态更新
  • 给异步任务增加超时控制

setTimeout

setTimeout(callback, delay) 会在延迟指定毫秒后执行一次回调。

js
const timerId = setTimeout(() => {
  console.log('run later')
}, 1000)

取消超时任务

js
clearTimeout(timerId)

如果在回调执行前调用 clearTimeout(),这次任务就不会再运行。

传参与最小延迟

js
setTimeout(
  (name) => {
    console.log(name)
  },
  500,
  'Lee',
)

需要注意:

  • delay 不是“精确触发时间”,而是“最早会在这么久之后进入任务队列”
  • 真正执行还要等当前调用栈清空、前面的任务执行完

setInterval

setInterval(callback, delay) 会按固定间隔重复执行回调。

js
const intervalId = setInterval(() => {
  console.log('polling...')
}, 1000)

取消轮询

js
clearInterval(intervalId)

setTimeoutsetInterval 的区别

  • setTimeout:执行一次
  • setInterval:持续重复执行

如果任务本身耗时不稳定,很多场景下更推荐“递归 setTimeout”而不是 setInterval

js
function poll() {
  setTimeout(async () => {
    await request()
    poll()
  }, 1000)
}

poll()

这样做的好处是,只有上一次任务结束后才会安排下一次,避免多个请求重叠。

定时器和事件循环

定时器回调不会立刻执行,它会在延迟结束后进入宏任务队列,等待事件循环调度。

js
console.log(1)

setTimeout(() => {
  console.log(2)
}, 0)

console.log(3)

输出通常是:

js
1
3
2

因此 setTimeout(fn, 0) 的语义不是“立即执行”,而是“当前同步任务结束后尽快执行”。

常见问题

为什么定时器不准

原因通常包括:

  • 当前线程正在执行其他任务
  • 前面还有未执行的宏任务
  • 浏览器为了节能会对后台标签页降频
  • 回调本身执行时间超过了设定间隔

超时控制示例

js
function withTimeout(promise, ms) {
  return new Promise((resolve, reject) => {
    const timerId = setTimeout(() => {
      reject(new Error('timeout'))
    }, ms)

    promise
      .then(resolve)
      .catch(reject)
      .finally(() => {
        clearTimeout(timerId)
      })
  })
}

这类模式常用于请求超时封装。

使用建议

  • 一次性延迟任务用 setTimeout()
  • 周期任务先评估是否会发生重叠,必要时用递归 setTimeout() 替代 setInterval()
  • 组件卸载、页面切换或任务取消时,记得清理定时器。
  • 不要把定时器当成高精度调度工具。

参考资料

基于 MIT 许可发布