Event Loop(事件循环) 是 JavaScript 实现非阻塞 I/O 模型的核心机制,它使得 JavaScript 能够在单线程环境下处理异步操作(如定时器、网络请求、用户交互等),而不会阻塞主线程。
----------------------
为什么需要 Event Loop?
JavaScript 是单线程语言,意味着它一次只能执行一个任务。如果某个任务(比如一个耗时的网络请求)阻塞了线程,整个页面就会“卡住”。为了应对这个问题,JavaScript 引入了 事件循环(Event Loop) 来协调同步任务和异步任务的执行顺序。
------------------
Event Loop 的基本组成
1、调用栈(Call Stack)
- 用于记录当前正在执行的函数。
- 后进先出(LIFO)结构。
2、任务队列(Task Queue / Callback Queue)
- 存放宏任务(Macrotask) 的回调函数,比如:
- setTimeout、setInterval
- DOM 事件
- I/O 操作
- UI 渲染
3、微任务队列(Microtask Queue)
- 存放微任务(Microtask) 的回调函数,比如:
- Promise.then/catch/finally
- MutationObserver
- queueMicrotask()
--------------------
Event Loop 的执行流程
1、执行同步代码,将函数压入调用栈。
2、遇到异步操作(如 setTimeout 或 Promise),将其交给浏览器的 Web API 处理。
3、当异步操作完成时:
- 宏任务 → 进入宏任务队列
- 微任务 → 进入微任务队列
4、调用栈为空时,事件循环检查:
- 先执行微任务队列中所有任务(全部执行完,不留一个)。
- 再从宏任务队列中取一个任务执行。
5、重复上述过程。
-------------
举个例子说明执行顺序
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 是微任务,进入微任务队列。
- 同步代码执行完后,事件循环先清空微任务队列(输出 3),再执行一个宏任务(输出 2)。
--------
总结
关键点:每执行完一个宏任务后,会清空所有当前的微任务,然后再取下一个宏任务。
--------------------
扩展知识
- Node.js 中的 Event Loop 更复杂,分为多个阶段(timers、pending callbacks、poll、check 等)。
- 浏览器中的 Event Loop 相对简单,但同样遵循“宏任务 → 微任务”的循环机制
----------------
一句话总结
Event Loop 是 JavaScript 用来协调同步任务、异步回调执行顺序的机制,确保非阻塞的同时保持代码有序执行。