定时器任务不会准时执行,而是至少延迟设定时间后被放入宏任务队列,在当前调用栈清空且微任务队列清空后,于下一次事件循环中按fifo顺序调度执行。

JavaScript 事件循环中,定时器任务(如 setTimeout 和 setInterval)由浏览器或 Node.js 运行时的定时器系统管理,但**不会在设定时间一到就立即执行**,而是被放入宏任务队列(macrotask queue),等待当前调用栈清空、且上一个宏任务执行完毕后,才在下一次事件循环迭代中被调度。
定时器不是“准时”的,而是“至少延迟”
你传给 setTimeout(fn, 10) 的 10 毫秒,表示「最少等待 10 毫秒」,实际执行时间取决于:
- 当前调用栈是否繁忙(比如有长时间运行的同步代码)
- 事件循环是否正忙于处理其他宏任务(如 I/O、渲染、其他定时器)
- 浏览器对嵌套定时器的节流(例如页面非活跃时,
setTimeout最小间隔可能被拉长到 1000ms)
定时器回调进入宏任务队列,而非直接执行
当定时器到期时,运行时会把它标记为「可执行」,但真正调度发生在:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 当前同步代码执行完,调用栈为空
- 微任务队列(如
Promise.then、MutationObserver)全部清空后 - 事件循环进入下一个宏任务阶段,从宏任务队列头部取出一个任务(包括到期的定时器)执行
这意味着:即使一个 setTimeout(fn, 0) 到期了,它也得等所有微任务跑完,才能执行。
多个定时器按插入顺序排队,但受延迟影响
如果两个定时器分别设为 10ms 和 5ms,但 5ms 那个因主线程阻塞未能及时加入队列,那么它可能比 10ms 的还晚执行。关键点是:
- 定时器到期时间由底层系统记录(如系统时钟 + 延迟补偿)
- 到期后,任务被推入宏任务队列尾部(FIFO)
- 队列中的任务按排队顺序,逐个在后续事件循环中执行
Node.js 与浏览器略有差异
Node.js 使用 libuv 管理定时器,其事件循环有更明确的阶段划分(timers 阶段专门处理到期定时器)。而浏览器没有严格“timers 阶段”,但行为逻辑一致:定时器回调属于宏任务,在宏任务调度时机统一处理。注意:
- Node.js 中
setImmediate()和process.nextTick()属于不同队列(后者甚至优先于微任务) - 浏览器中没有
setImmediate,可用MessageChannel模拟类似微任务级调度
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










