事件循环是浏览器或node.js提供的协调调用栈、任务队列与渲染的机制;其核心为调用栈+宏/微任务队列,每轮执行一个宏任务后清空微任务队列,故微任务总先于宏任务执行。

JavaScript 的事件循环(Event Loop)是理解异步执行的关键。它不是 JavaScript 引擎本身的功能,而是由浏览器或 Node.js 运行时提供的机制,用来协调调用栈、任务队列和渲染更新之间的协作。
调用栈 + 任务队列 = 事件循环的核心结构
JavaScript 是单线程语言,所有同步代码都在**调用栈**中顺序执行。当遇到异步操作(如 setTimeout、Promise.then、用户点击等),JS 引擎不会卡住等待结果,而是把回调函数交给宿主环境(如浏览器)去处理。宿主环境在条件满足后,将回调放入对应的任务队列中:
-
宏任务队列(Macrotask Queue):包括
setTimeout、setInterval、setImmediate(Node.js)、I/O、UI 渲染等 -
微任务队列(Microtask Queue):包括
Promise.then/catch/finally、MutationObserver、queueMicrotask
事件循环每轮只从宏任务队列中取出一个任务执行;但一旦开始执行,就会在返回控制权给宿主前,**清空整个微任务队列**。
一次完整的事件循环运行流程
以浏览器为例,一次典型的循环步骤如下:
- 执行当前宏任务(如一个
setTimeout回调) - 执行过程中产生的所有微任务(如多个
Promise.then)被加入微任务队列 - 宏任务执行完后,立即逐个执行微任务队列中的所有任务(直到为空)
- 浏览器可能在此之后进行 UI 渲染(是否渲染取决于是否需要更新)
- 从宏任务队列中取下一个任务,重复上述过程
微任务总比宏任务先执行
这是最容易混淆的点。看这个例子:
console.log(1); setTimeout(() => console.log(2), 0); Promise.resolve().then(() => console.log(3)); console.log(4);
输出顺序是:1 → 4 → 3 → 2。原因:
-
1和4是同步代码,立刻进调用栈并打印 -
setTimeout回调进入宏任务队列,等待下一轮循环 -
Promise.then回调进入微任务队列,本轮宏任务结束后立即执行
注意:不同 Promise 状态触发时机不同
Promise.resolve().then() 是微任务;但 new Promise((resolve) => resolve()).then() 同样是微任务——因为 resolve() 是同步调用,then 回调仍被排入微任务队列。而 Promise.reject() 未被捕获时会触发 unhandledrejection,属于微任务,但错误报告行为本身不阻塞流程。
另外,async/await 本质是 Promise 语法糖,await 后面的表达式若返回 Promise,则后续代码会被包装成 .then,因此也属于微任务调度。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











