JavaScript事件循环按“一次宏任务+全部微任务”循环调度异步任务;嵌套Promise的.then回调均属本轮微任务,立即入队并连续执行,而嵌套setTimeout则逐层追加宏任务,每次仅执行一个。

JavaScript 事件循环本身不“处理”嵌套的异步任务,而是按规则调度它们——关键在于理解宏任务与微任务的嵌套触发如何层层加入队列,并被逐轮清空。
嵌套 Promise 不会打断微任务队列的连续执行
每次一个 Promise 链中调用 .then() 或 .catch(),返回的新 Promise 的回调会被立即加入微任务队列。即使这些回调内部又创建了新 Promise,它们的 then 回调仍属于本轮微任务的一部分,会在当前宏任务结束后、下一轮宏任务开始前全部执行完。
- 例如:
Promise.resolve().then(() => { console.log(1); return Promise.resolve().then(() => console.log(2)); }).then(() => console.log(3));输出顺序是 1 → 2 → 3,因为所有 .then 回调都在同一轮微任务阶段被依次取出执行 - 注意:return 后的 Promise.then 不会延迟到下一轮事件循环,它只是让新的微任务排在当前微任务队列末尾,但仍属于本轮清空范围
setTimeout 嵌套会不断产生新宏任务
setTimeout 每次调用都向宏任务队列追加一个新任务,而宏任务每次只取一个。因此嵌套 setTimeout 实际上是把多个宏任务“排队”,每个都要等前一个宏任务(含其全部微任务)完成后才执行。
- 比如:
setTimeout(() => { console.log('a'); setTimeout(() => console.log('b'), 0); }, 0);中,'b' 一定在 'a' 之后,且中间可能穿插其他宏任务(如用户点击、渲染) - 嵌套层级不影响优先级,只影响入队时机和排队位置;它不会让内层 setTimeout 变成微任务
async/await 的嵌套本质仍是 Promise 链展开
写成 async function f() { await f1(); await f2(); } 看似同步,但底层等价于 f1().then(() => f2())。每次 await 后的表达式若为 Promise,就会生成一个微任务来恢复函数执行。
立即学习“Java免费学习笔记(深入)”;
- 嵌套 async 函数调用,比如
async function outer() { await inner(); },inner 返回的 Promise 决议后,outer 的后续代码作为微任务加入队列 - 如果 inner 内部还有 await,那它的微任务会先于 outer 的后续微任务执行——因为 inner 的 Promise.then 总是比 outer 的 await 恢复更早入队
避免意外延迟的关键:别混淆“嵌套”和“队列层级”
事件循环不关心代码是否缩进或嵌套书写,只看任务类型和入队时机。真正决定执行顺序的是:
- 任务被放入哪个队列(宏任务 vs 微任务)
- 入队时刻(是同步执行中产生的,还是由上一个回调触发的)
- 当前调用栈是否为空(这是事件循环启动调度的唯一条件)
所以所谓“嵌套异步”,其实是一连串任务按规则进入不同队列的过程,不是一层包一层的结构,而是一个线性调度链。


















