理解事件循环 副本 来源:https://my.feishu.cn/docx/GRkudfUEeoMcoUxrH4Jcs90snIe 采集状态:已完成正文采集;已滚动到底部并复查 谈到 JavaScript 的事件循环,我们常常会想到一个经典的抽象模型:一个执行栈、一个任务队列、一个不断从队列里取任务并执行的循环。这个模型对于理解异步行为至关重要,但它也常常让我们止步于此,忽略了一个更深层的问题:这个循环究竟运行在浏览器的哪一部分?它和渲染、用户交互、网络请求之间,又是如何协同工作的?​ ​ 要真正掌握事件循环,我们需要跳出纯粹的 JS 语境,站在浏览器整体架构的视角,看看它真正的调度机制。​ ​ 接下来,我们将从五个层面,层层递进,理解事件循环​ 进程与线程的定义 ​ 在展开事件循环的位置之前,我们需要先澄清两个基础概念:进程(Process) 和 线程(Thread),这样后面讲浏览器架构时才不会混淆。​ 进程是资源分配的最小单位。​ 进程是操作系统为运行中的程序分配的一整套独立资源环境。它拥有独立的内存地址空间和必要的系统资源,作为程序执行的基本单位,用来保证不同程序之间彼此隔离、互不影响。​ 线程是 CPU 调度的最小单位。​ 一个进程内部可以包含多条线程,它们共享进程的内存空间。线程更轻量,负责执行具体的任务。​ 在浏览器里:​ • 打开一个新的标签页,会创建一个新的渲染进程;​ • 而渲染进程内部,会包含多条线程:主线程、合成线程、光栅线程等​ ​ 用浏览器的架构理解事件循环​ ​ 要理解事件循环,我们首先要理解现代浏览器(如 Chrome)的多进程架构。一个浏览器实例通常包含多个进程,各自承担不同职责, 我们主要关系两个进程:​ • 浏览器进程:这是浏览器的主干,负责管理窗口、标签页、地址栏、网络请求和进程间协调。​ • 渲染进程:每个标签页通常拥有一个独立的渲染进程​ 在每个渲染进程中,都有一条至关重要的主线程。这条主线程执行绝大部分工作,包括:​ • JavaScript 代码的执行(由 V8 引擎负责)​ • DOM 树的构建与操作​ • CSS 样式的计算、布局(Layout)及部分绘制(Paint)​ • 以及我们今天的主角: 事件循环的调度逻辑​ ​ 除此之外,渲染进程内部还包含若干 独立线程,它们与主线程协同工作,以提升整体渲染性能:​ • 合成器线程:负责图层的合成与位置计算,处理如滚动、transform 等无需重新布局的快速操作。在主线程忙于执行 JS 时,它仍能保持页面的部分交互流畅。​ • 光栅化线程:负责将图层内容进行光栅化,把矢量信息转换成位图纹理,为最终绘制做准备。​ ​ 同时,浏览器中还存在一个与渲染进程密切协作的 GPU 进程:​ • GPU 进程负责处理最终的图层合成与硬件加速绘制,将光栅化后的内容提交给 GPU 渲染。​ • 对于动画、3D、CSS transform 等需要大量计算和图形处理的场景,GPU 能显著减轻主线程和 CPU 的负担。​ • 独立的 GPU 进程还能提升浏览器稳定性,即使 GPU 驱动发生异常,也不会导致整个浏览器崩溃。​ ​ 主线程、独立线程与 GPU 进程共同构成了现代浏览器高性能渲染流水线,使页面在结构计算、绘制和合成之间实现真正的并行协作。​ ​ 因此,我们可以得出一个关键的结论:​ 事件循环是渲染进程的主线程上的一种调度算法。 它的核心职责,是决定在何时执行哪段 JS 代码、何时处理微任务、以及何时将控制权交给渲染引擎,从而驱动整个页面的生命周期。​ ​ ​ 画板 ​ ​ 一帧之内,事件循环做了什么?​ 为了实现流畅的视觉体验,浏览器致力于达到约 60 FPS 的刷新率,这意味着每一帧的渲染周期大约为 16.6 毫秒。在这短暂的时间里,事件循环处理了一系列复杂的任务。​ 我们可以将一帧内主线程的工作流程,简化为以下几个步骤:​ 1. 取出一个宏任务​ 事件循环会从宏任务队列中,选取最老的一个任务来执行。这些任务的来源多种多样,例如: ◦ 页面加载时执行的全局