Web Worker 的 postMessage 是结构化克隆加异步事件投递,数据不共享;须先绑定 onmessage 再 postMessage,否则消息丢失;transfer 可零拷贝传 ArrayBuffer;self.postMessage 语义更清晰;通信为单向事件驱动,非 RPC。

Web Worker 的 postMessage 不是简单“发个消息”,而是触发一次结构化克隆 + 异步事件投递,数据在主线程和 Worker 之间永远不共享、不可见、不可修改对方副本。
为什么先绑定 onmessage 再调用 postMessage 才能收到消息
Worker 启动后立即执行脚本,但主线程的事件监听器如果还没注册,postMessage 发出的消息就没人接收——浏览器不会缓存首条消息。这不是 bug,是设计使然:消息机制完全异步且无队列保障。
-
worker.onmessage = handler或worker.addEventListener('message', handler)必须在worker.postMessage()之前完成 - 推荐用
addEventListener:支持多次绑定,避免被后续赋值覆盖 - Worker 内部也一样:必须先写
self.onmessage = ...或self.addEventListener('message', ...),再处理业务逻辑
postMessage 传什么数据会“炸”或静默失败
结构化克隆算法(Structured Clone Algorithm)决定哪些值能传、哪些会被丢弃或报错。不是所有 JS 类型都支持。
- ✅ 安全传输:
String、Number、Array、Object(不含函数/循环引用)、Date、RegExp、Blob、File、ArrayBuffer、TypedArray - ❌ 直接报错:
function、undefined、Symbol、DOM node(如document.getElementById(...))、window、document - ⚠️ 看似成功实则丢失:
Map、Set、Promise在部分旧浏览器中被忽略或转为空对象
大数组怎么传才不卡主线程:用 transfer 参数零拷贝
传一个 10MB 的 ArrayBuffer,默认会完整克隆一份,主线程和 Worker 各持一个副本——内存翻倍,还拖慢序列化。用 transfer 把所有权移交过去,原线程立刻失去访问权,实现真正零拷贝。
立即学习“前端免费学习笔记(深入)”;
- 主线程发送时写法:
worker.postMessage(myArrayBuffer, [myArrayBuffer]) - Worker 收到后,
myArrayBuffer在主线程中变成ArrayBuffer(但.byteLength === 0),Worker 中可直接读写 - 只能 transfer 一次;transfer 后不能再传回原线程(除非再 clone)
- 常见误写:
worker.postMessage(data, [data.buffer])—— 如果data是Uint8Array,得明确取data.buffer,否则报错
Worker 里该用 postMessage 还是 self.postMessage
两者在 Worker 全局作用域中行为一致,但语义和可维护性差很多。
-
self.postMessage明确指向当前 Worker 的全局对象,和主线程的window.postMessage形成对称,不易混淆 -
postMessage是隐式调用self方法,在复杂嵌套或未来迁移到 SharedWorker 时容易出错 - Worker 中没有
window,所以写window.postMessage会直接抛ReferenceError - 主线程里不能用
self.postMessage向 Worker 发消息——必须通过worker.postMessage()实例方法
最容易被忽略的一点:通信是单向事件驱动,不是 RPC。你不能指望 worker.postMessage() 返回一个 Promise 或等待响应;所有交互必须靠约定好 type 字段 + 多次 postMessage + 主动监听来模拟请求-响应流。稍一松懈,就会写出“发了但没收到”“收到但不知道是哪次请求”的逻辑债。



















