setTimeout、setInterval 的延迟为什么不准确?如何避免任务堆积?

先理解原理和示例,再练习完整口述与追问。

延迟是调度门槛而非执行承诺;主线程、后台限速和嵌套最小间隔影响时间;异步轮询可重叠;根据时间戳校正。

setTimeout 的 delay 表示计时满足条件后允许调度任务,并不保证精确在该时刻执行。当前同步代码、其他任务、页面后台限速和系统调度都可能让回调晚于预期。在浏览器里,连续嵌套定时器到一定层级后,小延迟会被钳制到至少约 4ms;不是所有第一次 setTimeout 都固定 4ms,Node 的定时器规则也不能直接套用浏览器规则。

setInterval 不会等待 async 回调中的请求完成。若间隔 1 秒而请求耗时 3 秒,就可能同时存在多个请求;JavaScript 同步回调仍在同一线程执行,所谓重叠主要是未完成的异步工作。需要串行轮询时,在一次操作完成的 finally 中安排下一次 setTimeout,并在页面离开或组件卸载时清理定时器与取消请求。

倒计时不要每次机械减 1,而应保存目标时间,以目标时间减当前时间计算剩余量,页面恢复可见时重新计算。测量一段耗时通常用单调的 performance.now,跨页面展示截止日期则需要约定服务器时间与时钟偏差;动画优先 requestAnimationFrame,仍按实际时间差推进,不能假设每帧固定 16ms。

参考:MDN setTimeout。

一次轮询请求耗时 3 秒,setInterval 设为 1 秒:第 1 秒的请求未结束,第 2、3 秒又可能发新请求。改成 await 本次操作后再 setTimeout,可让未完成请求数保持一轮;如果要固定计划时刻,需要另用时间戳计算下一次,而不是把“完成后等一秒”误认为固定频率。

长同步回调也会推迟计时器。页面离开时清理定时器并取消请求,不能只清掉未来定时器却忽略已进行中的操作。

setTimeout 的 delay 表示计时满足条件后允许调度任务,并不保证精确在该时刻执行。当前同步代码、其他任务、页面后台限速和系统调度都可能让回调晚于预期。在浏览器里,连续嵌套定时器到一定层级后,小延迟会被钳制到至少约 4ms;不是所有第一次 setTimeout 都固定 4ms,Node 的定时器规则也不能直接套用浏览器规则。