微任务队列在每个宏任务结束后立即被同步、彻底清空,且必须清空完毕才进行ui渲染;清空过程采用while循环动态追加、耗尽为止,严格按宏任务隔离,确保promise链原子性与规范强制语义。

清空时机:紧接宏任务结束,早于UI渲染
只要当前宏任务的同步代码执行完(调用栈完全清空),引擎就立刻进入微任务处理阶段。它不等、不拖、不看时间,也不管有没有新宏任务已就绪——哪怕 setTimeout(0) 已排队,也得等到微任务队列全空才轮到它。这个阶段发生在 UI 渲染之前,确保所有 Promise 链、queueMicrotask 或 async/await 的后续逻辑都已落地,页面才可能更新。
清空方式:同步耗尽,动态追加
引擎用一个不可中断的 while 循环持续取任务、执行、再判断是否为空。关键点在于:
- 不是按“初始数量”执行固定几轮,而是以队列为空为唯一退出条件
- 执行过程中产生的新微任务(比如 .then 里又调用了 Promise.resolve().then)会直接插到队尾,继续被本轮循环处理
- 这意味着 Promise 链能连贯输出,不会因中途插入而断开时序
清空范围:按宏任务隔离,互不干扰
每个宏任务结束后清空的,只是它自己这一轮产生的微任务。例如:
- 页面首次加载的 <script></script> 代码里创建的 Promise.then,属于第一轮微任务
- 该脚本里触发的 setTimeout 回调执行后产生的 Promise.then,则属于第二轮,和第一轮无关
- 上一轮没清完?不可能——引擎保证每次只启动一次清空流程,且必须清完才走下一步
为什么必须清空?不只是优先级问题
这不仅是“让 Promise 比 setTimeout 快”的表层现象,更是语义保障:
- 如果只执行一个微任务,Promise.resolve().then(() => console.log(1)).then(() => console.log(2)) 就会拆成两轮,破坏链式原子性
- 浏览器渲染管线把“微任务清空”当作一个硬性前置条件,未完成就不触发帧更新
- ECMAScript 规范明确要求宿主环境必须执行 microtask checkpoint,这是强制契约,不是可选优化











