JavaScript是一门单线程语言,这意味着它一次只能执行一个任务。事件循环是支撑其能够处理异步操作、非阻塞I/O和用户交互的核心机制。它像一个协调员,决定了何时执行哪一段代码,使得网络请求、定时器等操作不会阻塞主线程。
![图片[1]-什么是事件循环(Event Loop)?-速码派](http://www.sumapai.com/wp-content/uploads/2026/01/1ae7fcd657b84e009d13d4bd2ebf03f9tplv-tb4s082cfz-aigc_resize_1080_1080-1.webp)
JavaScript运行时的基本结构
要理解事件循环,需先了解运行时的主要部分。调用堆栈用于跟踪当前正在执行的函数。当函数被调用,它被推入栈顶;执行完毕,则从栈顶弹出。堆用于动态分配内存,存储对象。任务队列则是一个待处理消息的队列,每个消息都关联着一个回调函数。
宏任务与微任务的执行顺序
异步任务并非立即执行,它们被分为宏任务和微任务。常见的宏任务包括setTimeout、setInterval、I/O操作和UI渲染。微任务则包括Promise的回调(.then, .catch, .finally)以及MutationObserver。
console.log('脚本开始');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('Promise');
});
console.log('脚本结束');
上述代码的执行顺序是:先打印“脚本开始”和“脚本结束”,然后打印“Promise”,最后才是“setTimeout”。这是因为在一次事件循环的迭代中,会先执行完所有微任务,再执行一个宏任务。
事件循环的工作流程
事件循环持续运行,其单次循环的基本流程是:从调用栈执行同步代码开始。当栈清空后,它会检查微任务队列并依次执行所有微任务,直到队列为空。接着,浏览器可能会进行UI渲染。最后,从宏任务队列中取出一个任务(如setTimeout的回调)推入调用栈执行,并开启下一个循环。
这个过程解释了为何Promise的微任务总在setTimeout的宏任务之前执行,即使定时器延迟设置为0。它也解释了在处理高耗时同步任务时,UI会失去响应的原因——因为调用栈被阻塞,事件循环无法进入下一轮去处理渲染或用户交互的宏任务。
在Node.js环境中,事件循环的实现基于libuv,其阶段划分更为复杂,但宏任务与微任务优先级的核心思想是一致的。掌握事件循环有助于你编写出性能更好、可预测性更强的异步代码。




















暂无评论内容