定时器回调超时不会叠加或追赶,而是串行排队执行,实际间隔拉长;setinterval不累积、不并发;递归settimeout更可控;主线程阻塞会导致所有定时器停摆;node.js中i/o不阻塞但cpu密集型任务仍拖慢事件循环。

定时器回调函数执行时间超过设定间隔时,不会叠加执行,也不会“追赶”错过的周期,而是按事件循环机制排队等待,实际执行间隔会拉长。
setInterval 的行为:不累积、不并发
当 setInterval 回调执行耗时 > 间隔时间,浏览器或 Node.js 不会启动新一次回调,而是等当前回调彻底结束,再把下一次回调推入任务队列。也就是说:
- 回调函数是串行执行的,不是并行或重叠的
- 如果回调耗时 800ms,而间隔设为 500ms,实际两次执行之间的时间差约等于 800ms(而非 500ms)
- 不会有“积压任务”自动补跑——错过的周期直接丢弃
setTimeout 模拟 setInterval 时更需警惕
用递归 setTimeout 实现周期逻辑(推荐做法)时,若回调执行超时,下一次 setTimeout 才会启动,天然避免堆积:
- 代码形如:
function tick() { doWork(); setTimeout(tick, 500); } - 即使
doWork()耗时 900ms,下一轮tick也会在它结束后才安排,不会提前触发 - 这种写法比 setInterval 更可控,尤其适合执行时间不确定的任务
主线程阻塞会让所有定时器“停摆”
如果回调里有长时间同步操作(比如百万级数组排序、死循环),整个 JavaScript 主线程会被占满:
- 后续所有定时器回调、用户点击、动画帧都会被挂起,直到该任务完成
- 这不是定时器“失效”,而是事件循环无法推进——浏览器可能弹出“脚本无响应”警告
- 解决思路:拆分任务(用 setTimeout 分片)、移入 Web Worker、或用 requestIdleCallback 让步给用户交互
Node.js 环境下的细微差异
在 Node.js 中,libuv 事件循环同样遵循“不抢占、不并发”原则,但 I/O 密集型任务(如文件读取、数据库查询)通常不阻塞主线程:
- 回调执行时间长,只影响同轮次的其他 timer 回调,不影响 pending 队列中的 I/O 事件
- 不过 CPU 密集型计算仍会拖慢整个事件循环,建议用 child_process 或 worker_threads 卸载











