宏任务执行严格依赖调用栈清空,仅在全局脚本执行完或每个宏任务结束后、栈为空时,事件循环才从队列头部取一个任务执行;常见宏任务包括settimeout、script代码块、ui渲染等。

宏任务的触发时机严格依赖于调用栈是否清空。只有当同步代码全部执行完毕、调用栈变为空,事件循环才会从宏任务队列中取出一个任务执行。
宏任务不会打断正在运行的同步代码
无论 setTimeout 设置为 0 毫秒,还是用户点击事件已就绪,只要调用栈里还有函数在执行(比如一个长循环或深层嵌套调用),宏任务回调就只能排队等待。它不会插队,也不会被提前调度。
- 函数调用层层入栈,返回时逐层出栈;栈未空,事件循环不介入
- 整个 script 标签本身就是一个宏任务,它的执行就是一次完整的“同步段”
- 长时间同步操作(如 for 循环 1e6 次)会卡住宏任务,导致页面卡顿、定时器延迟
每次只执行一个宏任务
事件循环每轮仅从宏任务队列头部取一个任务推入调用栈执行。这个任务可能又产生新的宏任务(如再调用 setTimeout),但新任务会排到队尾,不会立即执行。
- 常见宏任务:setTimeout、setInterval、I/O 回调、UI 渲染、postMessage、script 整体代码块
- 即使多个 setTimeout 同时到期,也按注册顺序依次执行,每次只拿一个
- 宏任务执行期间产生的微任务,要等它彻底结束、调用栈再次清空后才开始处理
调用栈清空是宏任务启动的必要前提
事件循环不会主动中断当前执行流。它只在两个关键节点检查任务队列:一是全局脚本执行完;二是每个宏任务执行完毕后。这两个节点的共同特征就是调用栈为空。
- 不是“时间到了就执行”,而是“栈空了才看队列里有没有可执行的宏任务”
- 宏任务执行过程中若触发新异步操作(如 fetch 或另一个 setTimeout),新任务加入对应队列,但不改变当前宏任务的执行节奏
- 渲染(paint)在浏览器中也被视为宏任务,所以它也必须等前一个宏任务和所有后续微任务完成才能发生
不复杂但容易忽略:宏任务的“触发”其实是被动的——它不主动抢占,只等待主线程腾出手来。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











