现代浏览器的多进程架构中,主要有两个核心进程:
- Browser Process 进程
- 有个 main thread: UI 处理 + IPC 通信
- 其他功能线程 + 线程池
- Render Process 进程
- 有个 main thread: DOM渲染 + JS 执行 (通过调用 V8 引擎) + IPC 通信
- 其他功能线程 + 线程池
上一篇已经介绍了 DOM 渲染的详细过程。这里主要详细拆解渲染进程如何和 V8 JS 执行引擎进行协同。
宿主环境 / Web APIs
有个很重要的前置概念,叫宿主环境,对于 JS 主要有两个宿主环境 (HOST ENV):
- 浏览器
- Node
浏览器宿主环境主要提供以下能力:
- Event loop 能力
- Web API (XHR / Fetch)
- callback 队列
- settime out、网络、IO、XHR 能力等
- 事件监听:addEventListener
- DOM BOM 对象
- 定时器(setTimeout / setInterval)
💡 AJAX 是什么?
- AJAX 不是浏览器的某个 API
- 它是指:使用 JavaScript 在不刷新页面的情况下和服务器交换数据,并动态更新网页内容的技术组合
- 例如动态操作 DOM,而不需要重新请求服务器重新加载整个 DOM。
- XHR 异步请求等
- 最早基于 XMLHttpRequest,但现在也可以基于 fetch、WebSocket 等
V8 引擎
V8 是 JavaScript 的运行时(Runtime)核心部分之一,但 它不是完整的运行时环境。
V8 主要负责:
- ✅ 解析 JavaScript 源代码,编译成机器码
- ✅ 执行代码(包括执行栈/上下文栈管理)
- ✅ 内存分配与垃圾回收(堆的基本分配)
- ✅ 处理语言内建的类型和操作(如数组、对象、闭包、作用域)
- ✅ 实现微任务队列(Promise 的 .then 等回调)

一些基本概念
1:XHR (宿主对象,由 Web API 标准制定,被浏览器实现和提供,不是 JS 原生的 API)
AJAX 概念的一部分,是早起的 Web API,由浏览器宿主提供,主要用来异步处理网络请求,它是通过回调注册的方式实现:
- 一般在 Renders Process 的主线程发起,一段 JS Code 执行 xhr 请求
- 底层会通过 IPC 调用 Browser Process 的 Network Sercies (从网络线程池中拿一个现成),发起请求
- 然后成功后,再次 IPC 调用回 Render Process 进程,回调回来的时候,注册宏任务。等待执行最终的回调。
JS 脚本执行 渲染进程 -----------------------|------------------------------- V8 执行 JS | V8 引擎发现 XHR 调用 ↓ Blink 实现调度请求(C++) ↓ 将请求通过 IPC 发给浏览器主进程 网络请求发生 浏览器进程 -----------------------|------------------------------- | Network Thread (或 Network Service) ↓ 发送 HTTP 请求,等待响应 ↓ 收到响应,通过 IPC 通知渲染进程 响应回调注册 渲染进程 -----------------------|------------------------------- 将回调加入事件队列(Task Queue) ↓ V8 的事件循环调度该任务执行 ↓ 执行 onreadystatechange / onload 等回调
一段简易的 XHR 代码:
const xhr = new XMLHttpRequest(); xhr.open("GET", "https://api.example.com/data"); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { console.log(xhr.responseText); // 获取到数据 } }; xhr.send();
2: Fetch (宿主环境提供,新的 Web API 标准,浏览器会提供,Node 也会模拟实现类似的 Fetch)
现代库的演化路径大致如下:
- 老一代封装:jQuery → $.ajax() 基于 XHR
- 中间阶段:Axios(兼容 XHR 和 Fetch)
- 新时代:原生 fetch + async/await(Promise)(不依赖库)
Node.js 没有浏览器的 Web API,所以 没有原生 XHR 或 Fetch,它用的是 http 模块或者 node-fetch、axios 这类库来实现网络请求。
✅ fetch 到网络响应回调的底层流程(详细)
🌐 架构背景图(文字版)
+----------------+ IPC +------------------------+ | 渲染进程 | <------------> | 浏览器进程(含 NetSvc) | | - JS 线程 | | - Network Thread | +----------------+ +------------------------+
📍 fetch 执行到回调的核心步骤拆解:
① JS 执行 fetch() 调用
- 渲染进程的主线程(JS 引擎)执行 fetch()。
- fetch() 由 宿主环境提供(绑定在 window 上),其底层是一个 C++ 实现的绑定接口。
- 内部会调用 Blink 引擎的 FetchManager。
② 渲染进程调用浏览器进程(通过 IPC)
- 渲染进程通过 IPC(进程间通信) 发消息给浏览器主进程,请求发起一个 HTTP 请求。
- 浏览器主进程中的 Network Service 模块接收请求,并在内部 异步发起网络请求(由 Network Thread 执行)。
③ 浏览器进程收到 HTTP 响应,准备发回渲染进程
- 网络响应到来,浏览器进程调用回调函数处理响应。
- 然后它再通过 IPC,把响应结果(或数据流)回传给渲染进程。
④ 渲染进程收到 IPC 响应:注册回调任务
- 渲染进程收到来自浏览器主进程的 IPC 消息。
- Blink 中的调度器(TaskRunner)将这个响应封装成一个异步任务。
- 这个任务将由 JS 主线程调度执行,通常被插入到 任务队列(microtask 或 macrotask)中。
这一步就类似于:
// 模拟意义上等价于 queueMicrotask(() => { promise.resolve(response); });
⑤ Promise.resolve 执行,加入微任务队列
- 一旦响应到达并且触发了 resolve(response):
- promise 状态从 pending 变为 fulfilled。
- .then(...) 注册的回调被调度加入 微任务队列。
- JS 栈清空后,微任务开始执行回调。
浏览器主线程是如何注册 Network Service 的回调?
- 严格来说,渲染进程并不直接“注册回调”在 Network Service 内部。
- 而是通过浏览器进程(负责调度网络请求)向渲染进程“派发异步事件(通过 IPC)”。
- 渲染进程收到 IPC 后,会由内部 C++ 绑定逻辑调度 JS 环境中的 resolve() 操作。
关键词 | 解读 |
渲染进程主线程 | 负责 JS 执行 + 调度异步回调 |
浏览器进程 | 管理网络资源、发起 HTTP 请求 |
IPC 回调 | 是渲染进程接收异步数据的关键机制 |
resolve 的触发 | 是渲染进程收到响应后执行的 |
微任务 | 是 JS 引擎接收到 resolve 后加入的 |
宏任务 / 微任务 / Promise / 事件循环
✅ 宏任务(Macro Task)队列
- 由宿主环境提供和维护。
- 宿主环境: 浏览器、Node.js 等,它们控制着整个事件循环。
- 常见的宏任务包括:
- setTimeout
- setInterval
- setImmediate(Node.js)
- MessageChannel 回调
- I/O 回调
- UI 渲染任务
✅ 微任务(Micro Task)队列
- 由 JavaScript 引擎(如 V8)提供支持,属于语言层面的特性。
- 在当前执行栈清空之后、下一个宏任务开始之前,V8 会立即清空微任务队列。
- 常见的微任务包括:
- Promise.then / catch / finally
- queueMicrotask
- MutationObserver(浏览器)
🧠 补充理解
- V8 本身 不控制事件循环(Event Loop),但它会提供执行微任务的能力。
- 宿主环境通过内嵌 V8,并控制事件循环逻辑(例如主线程的任务调度器),在栈清空后,调用 V8 提供的接口去执行微任务队列。
- 宏任务是调度机制的基本单位,微任务是栈尾附加的“补充任务”。