title: Web Worker 通信深度解析(底层原理版)
date: 2026-01-30
category: “JavaScript / 浏览器内核”


Web Worker 核心笔记

1. Web Worker 角色定位

  • 多线程模型:JavaScript 在后台开启独立线程,不占用主线程(UI 线程)。
  • 物理隔离:拥有独立的 Event Loop、独立内存、独立的全局对象(self/WorkerGlobalScope)。
  • 局限性:无法访问 DOM、localStoragewindow 对象。

2. postMessage 通信底层流程(核心)

跨线程通信并非“瞬间移动”,而是遵循 “打包 -> 投递 -> 排队 -> 执行” 的逻辑:

  1. 打包(发送方)
    执行 postMessage(data),浏览器利用“结构化克隆算法”对数据进行深拷贝。
  2. 入队(接收方)
    浏览器内核将“消息数据”与“处理逻辑”封装成一个 Message Task(宏任务),直接塞进接收方线程的 宏任务队列
  3. 触发执行(接收方)
    当接收方的事件循环(Event Loop)轮询到该任务时:
    • 取出任务:任务出队。
    • 即刻执行:直接触发 onmessage 回调函数。
    • 注入参数:将打包的数据作为 event.data 传入回调。

3. 核心知识点校准 ⚠️

  • 单一任务化postMessage 产生的任务本身就包含了回调逻辑。任务出队时,直接执行回调,不需要二次排队。
  • 异步性:发送消息是同步的调用,但接收消息永远是异步的(必须等当前执行栈清空、等 Event Loop 调度)。
  • 顺序性:同一个线程发出的多个 postMessage 任务,会严格按照发送顺序进入目标队列,并按顺序执行。

4. 事件循环模型对比

特性 主线程 DOM 事件 (click) Worker 消息事件 (postMessage)
任务类型 宏任务 (Macrotask) 宏任务 (Macrotask)
触发点 外部触发(用户/硬件) 内部触发(JS 代码指令)
调度流程 浏览器捕获 -> 任务入队 -> 循环执行 代码调用 -> 跨线程入队 -> 循环执行

5. 老师总结的简记口诀

发消息,即下单;
传数据,进队列;
轮到它,即执行;
不插队,不重复。


6. 进阶提示

  • 大数据传递:若数据量极大(如 100MB 数组),克隆会产生性能损耗,此时应考虑使用 Transferable Objects(如 ArrayBuffer)来转移所有权而非克隆。

这部分差异极其关键,它能帮你彻底分清“异步”的两种不同来源。我们可以把这个差异定义为 “被动监听”与“主动投递” 的区别。

这是为你准备的补充笔记,可以直接追加到之前的 .md 文件中:


7. 深度进阶:DOM 事件与 postMessage 的本质差异

虽然两者最终都表现为“宏任务”,但它们的生命周期入队机制完全不同:

A. DOM 事件:被动监听机制 (Reactive)

  • 逻辑addEventListener 只是在浏览器内核注册了一个“预案”,此时任务队列是空的
  • 入队时机:只有当外部事件(如用户点击、网络包到达)真实发生时,浏览器内核才会临时产生一个任务扔进队列。
  • 特点:任务的产生取决于外部环境,具有不可预测性。

B. postMessage:主动投递机制 (Proactive)

  • 逻辑postMessage 是一种显式的“跨线程任务派遣”。
  • 入队时机:当你执行代码的那一刻,任务就已经产生并进入对方队列了。它不等待外部触发,而是 JS 执行流的直接延伸。
  • 特点:任务的产生取决于内部代码逻辑,具有高度的可控性。

差异对比表

维度 DOM 事件 (如 Click) Web Worker (postMessage)
任务发起者 浏览器外部(用户/硬件) JavaScript 代码内部
队列状态 触发前队列为空,仅有监听器 调用即入队,任务立即排队
数据来源 浏览器生成的 Event 对象 JS 显式传递并克隆的 data
本质比喻 陷阱:踩到(点击)才会触发 快递:寄出(调用)即在路上

8. 底层总结(Archive 核心)

结论:
postMessage 并不是在“等待某个事件发生”,它本身就是创造任务的动作
这种差异决定了:

  1. DOM 事件是为了响应(Response);
  2. Web Worker 通信是为了协作(Collaboration)。

postMessage 的本质,就是跨越线程屏障,直接把“执行回调”这个任务,亲手塞进对方的宏任务队列里。