微任务队列采用链式执行而非插队,事件循环持续清空直至为空;每次宏任务后进入微任务检查点,递归执行并允许新微任务追加至队尾;常见添加方式包括then、queuemicrotask等,均遵循fifo顺序且不可干预优先级。

微任务队列中添加新微任务,事件循环会持续清空队列,直到没有新增微任务产生——不是“插队”,而是“链式执行”。只要当前轮次的微任务执行过程中又注册了新微任务(比如调用 then、queueMicrotask、MutationObserver 等),这些新任务会自动追加到队列末尾,并在本轮微任务清空阶段继续执行。
微任务是递归清空的
每次宏任务(如 setTimeout 回调、脚本初始化、用户事件)执行完毕后,事件循环会进入“微任务检查点”:它不会只执行一个,而是反复取出并执行队列头部任务,同时允许每个任务内部再添加新微任务。这个过程持续进行,直到队列彻底为空。
- 例如:
Promise.resolve().then(() => { console.log(1); Promise.resolve().then(() => console.log(2)); })→ 输出顺序一定是 1 → 2 - 即使嵌套多层,只要没新任务加入,队列就自然结束;若有条件分支控制是否添加,则退出时机由业务逻辑决定
常见添加方式及等效性
向微任务队列添加任务有多种标准手段,它们都触发同一底层机制:
-
Promise.resolve().then(callback)或Promise.reject().catch(callback) -
queueMicrotask(callback)(最语义清晰,推荐) -
MutationObserver(仅浏览器环境,监听 DOM 变更后回调) -
process.nextTick()(Node.js 专属,优先级略高于其他微任务) -
async/await中的await后续代码(本质是语法糖,底层仍走 Promise 链)
要注意的边界和风险
看似灵活的链式执行,实际暗含运行时约束:
-
无限递归会卡死主线程:比如在
then里无条件再次调用then,V8 会在约 1000 层后抛RangeError,这是引擎保护,不是规范行为 -
宏任务永远让路:所有新产生的微任务,无论何时加入,都必须等本轮全部执行完,才会轮到下一个宏任务(如
setTimeout、渲染、I/O 回调) - 没有优先级或手动干预接口:你无法“插队”、取消或调整顺序;所有微任务平等,按注册顺序(FIFO)执行
在 asyncio 中对应怎么做
Python 的 asyncio 不暴露微任务队列,但行为逻辑一致:
- 用
asyncio.create_task(coro)注册新协程,它会被当作微任务级别调度 - 若需强制让出、确保刚注册的任务被立即处理,可加
await asyncio.sleep(0) - 不能在同步函数或子线程中直接调用
create_task,必须有正在运行的事件循环上下文











