JavaScript事件循环是单线程通过调用栈、宏任务队列和微任务队列协作实现异步非阻塞的核心机制;同步代码入栈执行,异步回调由Web API处理后按宏/微任务优先级进入对应队列,每轮先清空微任务再取一个宏任务。
JavaScript 的事件循环(Event Loop)是它实现异步非阻塞行为的核心机制,不是靠多线程,而是靠单线程 + 任务队列 + 调用栈协作完成的。
调用栈与任务的执行顺序
JS 是单线程语言,所有同步代码都在一个调用栈(Call Stack)中按顺序执行。遇到函数调用就压栈,返回就出栈。一旦栈空了,引擎不会停,而是去检查有没有待处理的“任务”。
- 同步任务(如
console.log()、普通函数调用)直接进栈执行 -
异步任务(如
setTimeout、fetch、用户点击)不会卡住主线程,而是交由 Web API(浏览器)或 Node.js 环境处理 - 等异步操作完成,Web API 把对应的回调函数推入任务队列(Task Queue),等待调用栈空闲
宏任务与微任务的区别
任务队列其实分两类:宏任务(macrotask)和微任务(microtask),它们的执行优先级不同。
-
宏任务:包括
setTimeout、setInterval、I/O、UI 渲染、script标签整体等 -
微任务:包括
Promise.then/catch/finally、MutationObserver、queueMicrotask() - 每次调用栈清空后,先执行所有当前轮次的微任务(直到微任务队列为空),再取一个宏任务执行
这就是为什么 Promise 总比 setTimeout 先输出——它属于更高优先级的微任务队列。
事件循环的基本流程
用伪代码描述一次循环:
- 执行一个宏任务(比如主 script 或某个
setTimeout回调) - 执行过程中遇到同步代码 → 直接入栈运行;遇到 Promise → 立即执行 executor,then 注册进微任务队列;遇到
setTimeout→ 交由浏览器延时,到期后推入宏任务队列 - 宏任务执行完,检查微任务队列 → 逐个执行,直到清空
- 渲染(可选,浏览器环境下)→ UI 更新
- 从宏任务队列取下一个任务,重复上述过程
一个典型例子帮你理清顺序
看这段代码:
console.log(1); setTimeout(() => console.log(2), 0); Promise.resolve().then(() => console.log(3)); console.log(4);
输出顺序是:1 → 4 → 3 → 2。因为:
-
1和4是同步,立刻执行 -
Promise.then是微任务,排在当前宏任务结束后、下个宏任务前 -
setT是宏任务,要等本轮全部微任务走完,再等下一轮循环才执行
imeout
基本上就这些。理解事件循环不靠死记,关键是分清“谁进栈、谁进哪个队列、谁先被调度”。多写几个带 setTimeout 和 Promise 的小例子跑一跑,节奏就自然了。

imeout






