Web浏览器中的JavaScript事件循环机制
2024-09-03
1422 字约 5 分钟
...JavaScript是一门单线程语言,但通过事件循环(Event Loop)机制,它能够处理异步操作,实现非阻塞的I/O模型。让我们深入了解Web浏览器中的事件循环机制。
事件循环的基本概念
事件循环主要由以下几个部分组成:
- 调用栈(Call Stack):用于存储正在执行的函数调用。
- 堆(Heap):用于存储对象。
- 任务队列(Task Queue):
- 宏任务队列(Macrotask Queue)
- 微任务队列(Microtask Queue)
事件循环的流程
让我们用一个流程图来说明事件循环的工作原理:

- 首先,执行调用栈中的同步代码。
- 当调用栈为空时,检查微任务队列。
- 执行所有微任务直到微任务队列为空。
- 从宏任务队列中取出一个任务执行。
- 重复步骤2-4,直到所有任务都执行完毕。
宏任务vs微任务
理解宏任务和微任务的区别对于掌握事件循环机制至关重要。
宏任务包括:
- script(整体代码)
- setTimeout/setInterval
- UI渲染
- I/O操作
- postMessage
- MessageChannel
微任务包括:
- Promise的then/catch/finally回调
- MutationObserver回调
- queueMicrotask()
实例分析
让我们通过一个例子来理解事件循环的执行过程:
console.log('1');
setTimeout(() => {
console.log('2');
}, 0);
Promise.resolve().then(() => {
console.log('3');
});
new Promise((resolve) => {
console.log('4');
resolve();
}).then(() => {
console.log('5');
});
console.log('6');
执行顺序分析:
- 首先执行同步代码,输出:1, 4, 6
- 将setTimeout回调放入宏任务队列
- 将第一个Promise的then回调放入微任务队列
- 将第二个Promise的then回调放入微任务队列
- 同步代码执行完毕,检查微任务队列,依次执行微任务,输出:3, 5
- 微任务队列清空,执行下一个宏任务(setTimeout回调),输出:2
最终输出顺序:1, 4, 6, 3, 5, 2
async/await 在事件循环中的表现
首先,让我们看一个包含 async/await 的代码示例:
console.log('1');
async function asyncFunction() {
console.log('2');
await Promise.resolve();
console.log('3');
await Promise.resolve();
console.log('4');
}
setTimeout(() => {
console.log('5');
}, 0);
asyncFunction();
new Promise((resolve) => {
console.log('6');
resolve();
}).then(() => {
console.log('7');
});
console.log('8');
让我们分析这段代码的执行顺序:
- 首先执行同步代码:
- 输出 '1'
- 遇到
asyncFunction()调用,进入函数内部 - 输出 '2'
- 遇到第一个
await,将后续代码放入微任务队列,然后跳出asyncFunction - 将
setTimeout回调放入宏任务队列 - 执行 Promise 构造函数中的同步代码,输出 '6'
- 输出 '8'
- 同步代码执行完毕,检查微任务队列:
- 执行
asyncFunction中第一个await后的代码,输出 '3' - 遇到第二个
await,将后续代码再次放入微任务队列 - 执行 Promise 的
then回调,输出 '7'
- 执行
- 当前微任务队列执行完毕,再次检查微任务队列:
- 执行
asyncFunction中第二个await后的代码,输出 '4'
- 执行
- 微任务队列清空,执行下一个宏任务:
- 执行
setTimeout回调,输出 '5'
- 执行
最终输出顺序:1, 2, 6, 8, 3, 7, 4, 5
注意事项
- Promise构造函数中的代码是同步执行的。
- async/await是Promise的语法糖,await后面的代码相当于放在then方法的回调中。
- 每执行完一个宏任务后,都会检查并执行微任务队列中的所有任务。
结论
理解事件循环机制对于编写高效的异步JavaScript代码至关重要。通过合理安排宏任务和微任务,我们可以优化代码执行顺序,提高应用性能。在实际开发中,要注意区分同步任务、宏任务和微任务,以便更好地控制代码流程。下一篇我们一起看一下Node.js事件循环机制。
如果您觉得这篇文章有帮助,请点个赞吧~
相关文章
更多文章 →javascript2025-09-18
JavaScript 的多线程能力:Worker
如果你写过一些计算量稍大的 JavaScript 代码,比如图像处理、大量数据排序或者复杂的算法,你几乎肯定遇到过浏览器“卡死”的现象。点击页面没反应,动画也停了,就像整个世界都静止了。 这就是主线程被阻塞的典型后果。因为主线程既要负责执行 JavaScript,又要负责渲染页面、响应用户操作,一旦它被繁重的计算任务占满,就无暇顾及其他,用户体验便直线下降。 这个问题的根源,正是“主线程是单线程的”。那么,如何解决呢? 答案很简单:把这...
学习面试
javascript2025-09-10
手把手教你实现一个EventEmitter
你是不是也遇到过这种情况:代码里各种事件监听和触发,回调函数套了一层又一层,最后自己都理不清哪个事件先触发、哪个后触发了? 别担心,今天我就带你从零开始实现一个自己的事件触发器(EventEmitter),让你彻底掌握事件驱动的精髓,代码清晰度直接提升200%! 为什么要懂EventEmitter? 先说说为什么我们要关心这个。Node.js的核心就是事件驱动,比如文件读写、网络请求,都是通过事件来处理的。不会EventEmitter,...
学习面试
javascript2025-08-09
Web Worker 多线程魔法
你是否曾遇到过网页因为复杂计算而卡顿的情况?今天,让我们一起探索Web Worker的神奇世界,看看如何用多线程技术解决性能瓶颈,并实现流畅的图片压缩功能! 为什么需要Web Worker? 想象一下,你在一个繁忙的餐厅里,只有一位服务员(主线程)负责点餐、上菜、结账所有工作。当客人突然暴增时,这位服务员就会手忙脚乱,其他客人只能干等着… 这就是JavaScript的现状—— 单线程 !所有任务都在一个线程中排队执行,一旦遇到复杂计算(...
学习面试
javascript2025-08-07
🌐AST抽象语法树
AST 是前端开发中一个非常重要的概念,它就像是代码的“编译器”,能够帮助我们理解和操作代码。本文将深入探讨 AST 的原理和应用,带你从零开始了解 AST。 一、什么是 AST? AST(抽象语法树)是一种树形结构,用于表示程序代码的语法结构。它将代码分解为一系列的节点,每个节点代表代码中的一个元素,如变量、函数、表达式等。AST 是编译器和解释器的基础,也是代码分析和转换的关键工具。 1\. AST 的结构 AST 的结构通常包括以...
学习面试
javascript2025-08-05
打字机效果是如何实现的
分析效果 使用 AI 对话的时候,文字是一个字一个字蹦出来的?它不是等所有内容生成完毕后一次性展示,而是以一种流畅、实时的“打字机”效果呈现,这种体验的背后,隐藏着一种高效的Web技术。它就是 服务器推送事件(Server Sent Events,简称 SSE) 什么是 SSE 从根本上说,SSE 是一种允许服务器 单向 、 持续 地向客户端(浏览器)推送数据的Web技术。 可以把它想象成一个 电台广播 : 服务器 是广播电台,持续不断...
学习面试
javascript2025-08-04
Promise 从原理到实现
前言 是 异步编程的核心概念之一,它为我们提供了一种更优雅的方式来处理异步操作。 本文将全面剖析 的工作原理,并逐步实现一个符合 规范的 。 正文 一、Promise 基础概念 代表一个异步操作的最终完成(或失败)及其结果值。它有 种状态: 1. pending : ,既不是成功,也不是失败状态 2. fulfilled :意味着操作 完成 3. rejected :意味着操作 状态一旦改变就不能再变,只能从 变为 ,或从 变为 。 二...
学习面试
评论
请登录后发表评论
去登录