事件循环执行顺序为:同步代码→清空当前微任务队列→下一个宏任务→再清空微任务队列;微任务总在注册它的宏任务结束后立即执行,不因“嵌套”延迟。

辨别宏任务嵌套微任务的执行流,关键不是“数嵌套层数”,而是盯住事件循环的固定节奏:每个宏任务执行完 → 立即清空当前所有微任务 → 才轮到下一个宏任务。所谓“嵌套”,其实是新任务被推入对应队列的时间点不同,而非执行时的调用栈嵌套。
宏任务里注册的微任务,仍属于“本轮宏任务结束后立即执行”
只要微任务是在某个宏任务执行过程中(包括其同步代码、或其内部 setTimeout 回调里)创建的,它就会被加入当前微任务队列,并在该宏任务结束时一并执行,不会推迟到下一轮宏任务之后。
- 例如,在
setTimeout回调(一个宏任务)里调用Promise.resolve().then(...),这个then是微任务,但它会在该setTimeout回调执行完后、下一个宏任务开始前执行 - 即使这个
then里又调用了另一个Promise.then,新微任务也会被追加到当前微任务队列末尾,继续执行,直到队列清空
典型嵌套场景的执行拆解
看这段代码:
console.log('1');
setTimeout(() => {
console.log('2');
Promise.resolve().then(() => console.log('3'));
}, 0);
Promise.resolve().then(() => console.log('4'));
console.log('5');
执行流是:
- 同步执行:输出 1 → 5
- 宏任务结束,清空微任务队列:执行第一个
Promise.then→ 输出 4 - 进入下一轮宏任务:执行
setTimeout回调 → 输出 2 - 该宏任务结束,再次清空微任务队列:执行其内部的
Promise.then→ 输出 3
注意:“3”在“2”之后、“4”之后,不是因为“嵌套深”,而是因为它所属的微任务,是在第二个宏任务(setTimeout 回调)执行期间注册的。
警惕“伪嵌套”:微任务中再调度宏任务
微任务里调用 setTimeout 或 setInterval,产生的仍是宏任务,会被塞进宏任务队列尾部,要等当前所有微任务跑完 + 下一轮宏任务开始时才执行。
- 比如在
Promise.then里写setTimeout(() => console.log('a'), 0),'a' 一定排在所有已排队微任务之后,且至少比它外层的宏任务晚一轮 - 这种调度会拉长响应延迟,若用于 UI 更新,可能造成肉眼可察的卡顿
实用判断口诀
遇到一段含异步的代码,按三步走:
- 先标出所有同步代码执行顺序
- 再找出哪些语句注册了微任务(
Promise.then、queueMicrotask、MutationObserver),它们统一归到“当前宏任务结束后马上执行”的批次 - 最后找出哪些语句注册了宏任务(
setTimeout、setInterval、postMessage、事件监听回调),它们各自独立占一轮事件循环,彼此之间严格串行
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











