主线程与worker通过postmessage异步通信,依赖结构化克隆算法传递数据副本,支持字符串、数字、对象、arraybuffer等类型,不支持函数、dom节点等;数据隔离无共享,大数组可借transferlist零拷贝移交所有权。
主线程与 worker 之间用 postmessage 实现通信,本质就是靠结构化克隆算法(structured clone algorithm)完成数据副本传递,不共享内存、不互相影响,整个过程异步且安全。
结构化克隆支持哪些数据类型
浏览器自动对传入 postMessage 的数据执行结构化克隆,支持以下常见类型:
- 基本类型:字符串、数字、布尔值、
null - 复合对象:普通对象、数组、
Date、RegExp、Map、Set - 二进制数据:
ArrayBuffer、TypedArray、DataView、Blob、File、ImageData
不支持的类型包括函数、DOM 节点、undefined、Symbol、循环引用对象,以及 Error 实例。尝试传递会报错或被静默忽略。
数据是副本,不是引用
每次调用 postMessage(data),浏览器都会生成一份完整副本:
- 主线程修改原对象,Worker 收到的数据不会变
- Worker 修改收到的对象,主线程原始数据也不受影响
- 双方操作完全隔离,不存在“同步状态”或“响应式更新”
例如主线程发送 { count: 1 },Worker 接收后改为 { count: 2 },主线程里的那个对象仍是 { count: 1 }。
主线程与 Worker 的标准写法
双方都需要主动监听和发送,不能只写一边:
- 主线程创建 Worker 后,用
worker.onmessage或worker.addEventListener('message', ...)接收返回 - Worker 内部用
self.onmessage或self.addEventListener('message', ...)接收指令,并用self.postMessage()回传结果 - 推荐 Worker 中使用
addEventListener,避免多次赋值onmessage导致监听器被覆盖
消息体建议带 type 字段做路由,方便扩展多种任务类型,比如 { type: 'calculate', data: [...] } 或 { type: 'load-image', url: '...' }。
大数组传输可选零拷贝优化
当传输大量二进制数据(如图像、音频缓冲区)时,结构化克隆会完整复制 ArrayBuffer,带来性能负担。此时可用 transferList 参数移交所有权:
- 主线程发送:
worker.postMessage(buffer, [buffer]) - 发送后,原
buffer在主线程变为detached,不可再访问 - Worker 收到的是同一块内存地址,无拷贝开销
- 仅适用于
ArrayBuffer、MessagePort等可转移对象
注意:这不是“共享内存”,而是内存所有权的单向移交,仍需配合明确的消息协议来协调读写时机。










