javascript事件循环执行顺序为同步代码→微任务→宏任务:同步代码(如console.log)先执行;接着清空微任务队列(promise.then、async/await等);最后执行下一个宏任务(settimeout、dom事件等)。

JavaScript 是单线程语言,靠事件循环协调同步与异步代码的执行。它的核心不是“谁先写谁先跑”,而是严格遵循:同步代码 → 微任务 → 宏任务 的三级优先级顺序。
宏任务和微任务分别有哪些?
宏任务由宿主环境(浏览器或 Node.js)调度,每次事件循环只取一个执行:
- script 整体代码(第一个宏任务)
- setTimeout、setInterval
- DOM 事件(如 click、input)
- 网络请求回调(fetch、XMLHttpRequest)
- UI 渲染(浏览器中,在宏任务后、下一轮前触发)
微任务由 JS 引擎自身调度,优先级更高,会在每个宏任务结束后立即清空:
- Promise.then/catch/finally 中的回调函数
- async/await 中 await 后面的代码(等价于 .then)
- queueMicrotask() 显式添加的微任务
- MutationObserver(浏览器中监听 DOM 变化)
执行顺序到底怎么走?
每轮事件循环只处理一个宏任务,但会把所有待执行的微任务一口气跑完:
- 先执行当前宏任务里的同步代码(比如 console.log、变量赋值)
- 同步代码一结束,立刻检查微任务队列,按入队顺序依次执行,直到队列为空
- 微任务全部执行完,才从宏任务队列中取出下一个宏任务开始执行
- 新产生的微任务(比如在 Promise.then 里又 new 了一个 Promise),会加入本轮微任务队列尾部,仍算在“当前轮”内执行
容易踩坑的几个关键点
很多执行结果反直觉,问题往往出在对“同步/异步边界”的误判:
-
Promise 构造器内的代码是同步执行的,只有 then/catch 才进微任务队列。例如:
new Promise(r => { console.log('A'); r(); }).then(() => console.log('B'))输出 A、B,不是 B、A - await 前是同步,await 后是微任务。async 函数调用本身不产生异步,只是让 await 后逻辑延迟到微任务阶段
- setTimeout(fn, 0) 不等于“马上执行”,它只是尽快把 fn 推入宏任务队列,要等当前宏任务 + 所有微任务都跑完才轮到它
- Node.js 和浏览器略有差异:process.nextTick 在 Node 中优先级高于 Promise.then,但浏览器没有这个 API
一个典型例子帮你串起来
这段代码:
console.log('1');setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
输出顺序是:1 → 4 → 3 → 2。因为:1 和 4 是同步代码;3 是微任务,紧接在同步之后;2 是宏任务,排在微任务全部完成之后。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











