Promise回调属于微任务,每次宏任务执行完后立即清空所有当前微任务;输出顺序为1→6→5→2→4→3,嵌套Promise仅延长微任务链而不产生新宏任务。

在 JavaScript 中,宏任务中嵌套 Promise 时的执行顺序,本质是事件循环(Event Loop)对宏任务队列和微任务队列的调度规则决定的——每次宏任务执行完后,会清空当前所有微任务,再取下一个宏任务。关键不在“嵌套”,而在于 Promise 的 .then()、.catch() 回调属于微任务,且会在本轮宏任务结束后立即执行。
宏任务与微任务的基本队列行为
JavaScript 运行时维护两个关键队列:
-
宏任务队列(Macrotask Queue):包括
setTimeout、setInterval、I/O、UI 渲染、script整体(初始脚本)等;每次只取一个执行。 -
微任务队列(Microtask Queue):包括
Promise.then/catch/finally、queueMicrotask()、MutationObserver等;每次宏任务执行完毕后,**连续执行完所有当前微任务**,直到队列为空。
典型场景:setTimeout 中创建并链式调用 Promise
看这段代码的逐步推演:
console.log(1);
setTimeout(() => {
console.log(2);
Promise.resolve().then(() => console.log(3));
console.log(4);
}, 0);
Promise.resolve().then(() => console.log(5));
console.log(6);
执行步骤如下:
- 同步代码执行:
1 → 6 - 微任务队列加入:
5(来自全局 Promise.then) - 宏任务队列加入:
setTimeout 回调(延时为 0,但仍是宏任务) - 同步执行完,检查微任务队列 → 执行
5,微任务队列清空 - 取下一个宏任务 → 执行
setTimeout回调:2 → 4(注意:Promise.resolve().then(...)是同步创建 Promise 并把.then回调推入微任务队列,不是立刻执行) - 该宏任务执行完毕 → 再次检查微任务队列 → 执行
3
最终输出顺序:1 → 6 → 5 → 2 → 4 → 3
嵌套 Promise 不改变微任务本质
即使在 .then() 中再返回 Promise,也只影响微任务链的长度,不产生新宏任务:
Promise.resolve()
.then(() => {
console.log('a');
return Promise.resolve().then(() => console.log('b'));
})
.then(() => console.log('c'));
输出是 a → b → c,三者都在同一轮微任务阶段内依次执行。因为:
- 第一个
.then回调是微任务,执行后返回一个新的 Promise - 该 Promise 的
.then回调仍被加入**当前微任务队列末尾**(不是新宏任务) - 事件循环不会中断微任务执行,直到队列为空
容易混淆的点:async/await 与嵌套的关系
async 函数内部的 await 会让后续代码变成 Promise.then 的语法糖,仍属微任务:
setTimeout(() => {
console.log('outer');
async function inner() {
console.log('inner start');
await Promise.resolve();
console.log('inner end');
}
inner();
}, 0);
输出为:outer → inner start → inner end,其中 inner end 是微任务,在 setTimeout 宏任务结束后立即执行。
即使在 await 后再调用另一个 async 函数,只要没显式触发新的宏任务(如 setTimeout),就仍在微任务流中延续。
理解核心就一条:Promise 回调是微任务,它不“嵌套”进宏任务生命周期,而是被排入独立的微任务队列,并在每个宏任务之后集中、顺序、无中断地执行完。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











