理解事件循环 Event loop还是必须得理清楚渲染进程与事件循环的流程之间的关系:
1. GUI 渲染线程
- 职责:
- 负责渲染浏览器界面,解析 HTML、CSS。
- 构建 DOM 树、CSSOM 树、Render 树。
- 负责页面的布局(Layout/回流)和绘制(Paint/重绘)。
- 互斥关系:与 JS 引擎线程互斥。
- 当 JS 引擎执行时,GUI 渲染线程会被挂起(冻结)。
- GUI 更新会被保存到一个队列中,等 JS 引擎空闲时立即执行。
- 原因:防止 JS 在操作 DOM 的同时,GUI 还在渲染,导致渲染前后结果不一致。
2. JS 引擎线程
- 职责:
- 负责解析和执行 JavaScript 脚本程序:事件循环 Event loop(例如 V8 引擎)。
- 特性:
- 单线程:一个 Tab 页内只有一个 JS 引擎线程。
- 互斥关系:与 GUI 渲染线程互斥。
- 如果 JS 执行时间过长(如死循环),会导致页面渲染不连贯,出现页面响应阻塞(假死)。
3. 事件触发线程
- 职责:
- 归属于浏览器而不是 JS 引擎,用来控制事件循环 Event loop。
- 当 JS 执行到异步代码(如鼠标点击、setTimeout、AJAX 等)时,该线程会将对应的任务添加到任务队列的末尾。
- 触发时机:当事件满足触发条件时,将回调事件放入 JS 引擎的执行队列中等待处理。
4. 定时触发器线程
- 职责:
- 负责执行
setTimeout与setInterval的计时。
- 负责执行
- 原理:
- 非 JS 引擎计时:因为 JS 引擎是单线程的,如果处于阻塞状态会影响计时的准确性,因此由独立线程计时。
- 通知机制:计时完毕后,通知事件触发线程,将回调任务放入队列。
- 注意:W3C 标准规定,
setTimeout中低于 4ms 的时间间隔会被算作 4ms。
5. 异步 HTTP 请求线程
- 职责:
- 负责处理 AJAX、Fetch 等网络请求。
- 过程:
- 在连接后,通过浏览器新开一个线程进行请求。
- 检测到状态变更时,如果设置了回调函数,该线程会通知事件触发线程,将回调放入 JS 队列中执行。
6. Web Worker 线程
-
职责:负责执行耗时的计算任务,而不会阻塞主线程(JS 引擎线程)的 UI 响应。
-
创建机制:由 JS 引擎线程 通过
new Worker()指令开启。每创建一个 Worker,渲染进程就会多出一个对应的 Worker 线程。 -
与 JS 引擎线程的关系:
- 并行不互斥:与主 JS 引擎线程同时运行,互不干扰。
- 独立环境:Worker 线程有自己独立的内存空间、全局对象(
self而非window)和执行栈。 - 通信限制:无法直接操作 DOM,无法访问
window、document等对象。它与主线程之间通过 postMessage 机制进行数据传输(本质上是拷贝或转移内存)。