javascript异步靠事件循环协调调用栈、微任务队列和宏任务队列实现:同步代码在调用栈顺序执行,异步回调分入两类队列;每次调用栈清空后先清空全部微任务,再取一个宏任务执行。

JavaScript 的异步能力不是靠“多线程”实现的,而是靠调用栈、微任务队列、宏任务队列三者在事件循环协调下的精密配合。同步代码在调用栈里顺序执行,异步回调则按类型分发到不同队列,再由事件循环按优先级调度。
调用栈:只管同步,不等别人
调用栈是 JS 主线程唯一的执行容器,严格遵循后进先出(LIFO)原则。函数调用就入栈,返回就出栈,中间不会被打断。只要栈里还有任务,事件循环就不会去取新任务。
例如:
-
console.log('A')立即入栈、执行、出栈 -
setTimeout(() => console.log('B'), 0)不进栈,交给定时器线程处理,主线程继续往下走 -
Promise.resolve().then(() => console.log('C'))回调也不进栈,而是被放入微任务队列等待调度
微任务队列:当前宏任务结束后的“加急处理”
微任务具有最高优先级,它不排队等下一轮循环,而是在当前宏任务执行完毕、调用栈清空后立即全部执行,且必须清空整个队列才停止。
常见微任务包括:
Promise.then/catch/finally-
async/await内部的 resolve 处理 -
MutationObserver回调 -
queueMicrotask()显式插入的微任务
宏任务队列:事件循环每次只取一个
宏任务是事件循环的基本调度单位,每次循环只执行一个。执行完一个宏任务后,会立刻检查并清空微任务队列,然后才取下一个宏任务。
常见宏任务包括:
- 整体脚本(
<script></script>标签内容) -
setTimeout和setInterval回调 - I/O 操作(如 fetch 回调)、UI 渲染(浏览器环境)
-
setImmediate(Node.js)
执行顺序:一个清晰的循环节拍
一次完整的事件循环流程如下:
- 执行一个宏任务(比如初始脚本)
- 调用栈清空后,立即执行所有排队的微任务,直到微任务队列为空
- (浏览器环境下)可能触发 UI 渲染
- 从宏任务队列中取出下一个任务,开始下一轮
这个节奏决定了为什么 Promise.then 总比 setTimeout 先输出,哪怕延迟设为 0——因为前者是微任务,后者是宏任务。











