跳到主要内容

异步处理与UI渲染

· 阅读需 5 分钟

异步编程

异步编程是一种通过非阻塞方式处理任务的编程范式。不同语言通过不同机制实现异步:

  • JavaScript:单线程 + 事件循环 + 任务队列
  • Python:协程 + asyncio
  • Rust:通过 Future 特征(Trait)和执行器(Executor)。

单线程

JavaScript 的单线程是指其执行环境(如浏览器或 Node.js)在同一时间只能处理一个任务,所有代码按顺序在单个主线程上运行。

  • 单线程是 JavaScript 的设计基石,决定了其代码执行的顺序性和非阻塞异步的实现方式。
  • 通过事件循环和回调函数,JavaScript 在单线程上模拟了并发效果,以适合处理大量 I/O 操作(如网络请求、用户交互)
  • 对于 CPU 密集型任务,需通过 Web Worker子进程 突破单线程限制,避免阻塞主线程

JavaScript 异步模型

1.任务队列(Task Queue)

  • 宏任务队列(MacroTask Queue):setTimeout、setInterval、setImmediate(Node.js)、I/O 操作

  • 微任务队列(MicroTask Queue):Promise.then、async/await、MutationObserver(浏览器)、process.nextTick(Node.js)

    当调用栈为空时,事件循环开始调度:

    1. 优先处理微任务队列:处理所有微任务(包括微任务执行过程中新增的微任务)。
    2. 处理一个宏任务:从宏任务队列中取出一个任务执行。
    3. 重复步骤 1 和 2:再次检查并处理微任务队列,然后执行下一个宏任务,以此类推。

注意事项:

  1. setTimeout(fn, 0) 并非立即执行,而是在当前同步代码和微任务执行完毕后尽快执行(终究是个宏任务)。
  2. 大量微任务会阻塞 UI 渲染,因为微任务会在渲染前全部执行完毕。

2.事件循环(Event Loop)

  • 事件循环是实现异步编程的核心机制
  • 事件循环是异步操作的调度引擎,负责协调异步任务的执行顺序,确保单线程的 JavaScript 能高效处理并发请求
  • 调度模型:非抢占式

事件循环的工作流程

  1. 执行同步代码:主线程依次执行调用栈中的同步任务。
  2. 处理异步任务:遇到异步操作(如 setTimeout、Promise)时,将其交给 Web API 处理,主线程继续执行后续代码。
  3. 回调入队:异步操作完成后,其回调函数被放入相应的任务队列。
  4. 事件循环调度:
    当调用栈为空时,事件循环优先处理微任务队列(清空所有微任务)。
    然后从宏任务队列中取出一个任务执行,重复步骤 4。

UI 渲染

渲染流程

  1. 构建 DOM 树(DOM Tree);当浏览器解析 HTML 生成 DOM 树时,若遇到 <script> 标签,会暂停渲染,优先执行 JavaScript 代码(避免脚本修改未渲染的 DOM)
  2. 构建 CSSOM 树(CSS Object Model Tree);当浏览器解析 CSS 代码(包括 <style> 标签和外部样式表),生成 CSSOM 树时,会阻塞渲染。
  3. 生成渲染树(Render Tree):合并 DOM 树和 CSSOM 树,生成渲染树。
  4. 布局(Layout/Reflow):计算渲染树中每个节点的几何位置(如 offsetWidth、scrollTop 等),确定元素在页面中的尺寸和位置。
  5. 分层(Layerization):在每个层上,将元素的样式(如颜色、阴影、边框)转换为像素,生成位图。绘制顺序由层的堆叠顺序(z-index)和元素的绘制属性(如 clip-path)决定。
  6. 合成(Compositing),将多个层的位图合并到最终的屏幕图像中,处理层间的重叠和透明度。交给 GPU 处理,并输出到屏幕。

渲染与事件循环

事件循环(Event Loop)负责协调 JavaScript 执行、UI 渲染、网络请求等任务 JavaScript 执行与渲染互斥 :当 JavaScript 执行时(包括宏任务、微任务),UI 渲染会被暂停,直至 JavaScript 引擎释放主线程。反之,渲染过程中(如布局、绘制),JavaScript 代码也无法执行。现代浏览器的渲染引擎的部分操作会被分到独立线程:合成线程(Compositor Thread),负责图层合成(Compositing),与 GPU 交互,可并行处理滚动、动画等操作。

重排(Reflow)

当 DOM 的变化影响了元素的布局信息(元素的宽高、边距等几何信息)时,浏览器需要重新计算元素在视口内的位置和尺寸,这一过程称为重排或回流。

重绘(Repaint)

当一个元素的外观发生改变,但没有影响到布局信息时,浏览器会将新样式应用到元素上,这一过程称为重绘。

_ requestAnimationFrame(RAF)的回调函数会在渲染前执行,但它既不属于宏任务也不属于微任务,而是浏览器专门为动画优化的 API。搭配使用 transform 和 opacity 性能更好,因为它们不会触发回流,只会触发合成(Composite)。_

浏览器 DevTools 调试工具分析渲染性能

  • Performance 面板: 录制页面运行时的 CPU 占用、任务耗时、重排 / 重绘次数。 标识长任务(Long Tasks)和渲染瓶颈。
  • Rendering 面板(Chrome): 勾选 Paint flashing 高亮重绘区域。 勾选 Layout shift regions 检测元素位置偏移(CLS 指标)。
  • Layers 面板: 查看页面分层情况,分析层合并或拆分的合理性。