宏任务生命周期分为三阶段:未注册→已注册但未就绪(挂起)→已执行并退出;挂起时仅存于队列、不占CPU,就绪需满足触发条件,执行为原子性且不可中断。

异步任务触发后,宏任务本身并不立即执行,而是进入一个“等待调度”的生命状态——它已注册、未运行、可被取消,但尚未被事件循环选中。
宏任务的注册与挂起
当调用 setTimeout、setInterval、requestAnimationFrame 或处理 DOM 事件绑定时,浏览器或 Node.js 环境会将回调函数及其上下文封装为一个宏任务,并放入对应的宏任务队列(Macrotask Queue)中。此时该任务处于“挂起”(pending)状态:
- 它不占用主线程,也不消耗 CPU 执行时间
- 它依赖宿主环境(如浏览器定时器系统)维护倒计时或触发条件
- 在被事件循环取出前,它只是队列中的一个待执行条目,无执行上下文、无调用栈帧
宏任务的就绪与失效边界
宏任务从挂起到就绪,取决于其触发条件是否满足:
- setTimeout(fn, 0) 并非“立刻执行”,而是“最早在下一次事件循环开始时执行”——需等当前宏任务+全部微任务完成,且渲染可能已发生
- 用户点击事件 的宏任务,在事件冒泡结束、所有同步处理完成后才入队;若页面卡顿导致事件延迟派发,该宏任务的挂起时间也会延长
- 已注册但未触发的 setTimeout 可被 clearTimeout 主动取消,此时该宏任务生命周期终止,不会进入队列
宏任务的执行与退出
一旦事件循环选中该宏任务:
- 它被推入调用栈,获得完整的执行上下文(this、arguments、词法环境等)
- 同步代码逐行执行;过程中产生的新宏任务(如嵌套的 setTimeout)会重新进入挂起态
- 执行完毕后,该宏任务对象即退出生命周期——它的执行栈帧被弹出,相关闭包引用若无其他持有者,将进入垃圾回收候选
注意:宏任务没有“运行中暂停”状态
宏任务是原子性执行的——它一旦开始,就必须跑完全部同步部分,中间不能被其他宏任务打断(微任务除外)。它不像 Promise 那样有 pending/fulfilled/rejected 的内部状态机,也没有可查询的“当前进度”。它的生命状态只有三种:未注册 → 已注册但未就绪(挂起)→ 已执行并退出。

















