主题
定时器
浏览器提供的定时器能力主要包括 setTimeout 和 setInterval。它们不是 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)setTimeout 和 setInterval 的区别
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()。 - 组件卸载、页面切换或任务取消时,记得清理定时器。
- 不要把定时器当成高精度调度工具。
