javascript事件循环先执行宏任务再清空微任务队列,宏任务包括settimeout、setinterval、i/o、ui渲染和script代码块,微任务包括promise.then/catch/finally、queuemicrotask和mutationobserver回调。

JavaScript 事件循环的执行顺序,核心在于区分宏任务(macro-task)和微任务(micro-task),以及它们在每次循环中的执行优先级:每次宏任务执行完后,会清空当前所有微任务队列,再取下一个宏任务。
宏任务和微任务有哪些?
常见宏任务包括:setTimeout、setInterval、I/O、UI渲染、script整体代码块;
常见微任务包括:Promise.then/catch/finally、queueMicrotask、MutationObserver回调。
- 注意:Promise 构造器内的代码是同步执行的,只有 then/catch 才进入微任务队列
- async/await 本质是 Promise 语法糖,await 后面的表达式同步执行,await 之后的代码被包裹进 Promise.resolve().then()
- 多个微任务按注册顺序依次执行,不会被新加入的微任务“插队”(但新微任务会在本轮微任务清空后、下一轮宏任务前执行)
典型代码执行顺序分析步骤
遇到一段含异步操作的代码,按以下四步推演:
- 第一步:从上到下执行同步代码,遇到宏任务(如 setTimeout)就挂起,遇到 Promise 构造器就立即执行其回调函数
- 第二步:同步代码执行完,检查微任务队列是否为空;不为空则逐个执行,直到清空
- 第三步:取出一个宏任务(通常是最早入队的那个),执行它的回调
- 第四步:再次检查并清空微任务队列;重复第三、四步,直到宏任务队列也为空
容易踩坑的几个细节
很多面试题故意用嵌套 Promise 或混合 setTimeout 来制造干扰:
- Promise.resolve().then() 和 setTimeout(fn, 0) 谁先执行? → 微任务永远优于宏任务,即使时间设为 0
- await 后面跟的是普通值还是 Promise? → 如果是普通值,await 不会暂停;如果是 Promise,await 会让后续代码进入微任务
- setImmediate(Node.js)和 setTimeout(fn, 0) 在 Node 环境谁先? → setImmediate 属于宏任务,但排在本轮 I/O 回调之后、下轮 timer 之前,通常比 setTimeout(0) 晚
动手验证比死记更可靠
不要只靠背顺序表,直接在浏览器控制台或 Node REPL 中运行类似代码:
console.log(1); setTimeout(() => console.log(2), 0); Promise.resolve().then(() => console.log(3)); console.log(4);
输出是 1 → 4 → 3 → 2。对照上面四步推演,就能确认理解是否到位。遇到复杂嵌套,就拆成单步画队列变化图,比硬记更稳。











