Web Worker 使用 Transferable 后如何把结果传回主线程
来源:17golang原创
时间:2026-09-12 23:58:09 162浏览 收藏
Web Worker 使用 Transferable 传输二进制结果时,关键不是“把结果复制回来”,而是把结果的所有权再次交给主线程。最常见的写法是:主线程把 ArrayBuffer 放进消息,同时把同一个 buffer 放入 transfer 列表;Worker 处理后创建新的输出 buffer,再通过第二次 postMessage 转回主线程。转移完成后,原发送端的 buffer 会被 detach,不能继续当作可读写内存。
- Transferable 传的是资源所有权,不是复制;发送端的 ArrayBuffer 转移后
byteLength为 0。 - Uint8Array 这类 TypedArray 本身不是 Transferable,应该转移它的
buffer。 - 回传时让 Worker 创建独立输出 buffer,并在消息回调中重新建立主线程视图。
先把消息对象和转移资源对应起来
postMessage 的第一个参数是消息数据,第二个参数是要转移所有权的对象数组。transfer 列表只声明哪些资源要转移,并不会自动把资源塞进消息;因此消息里必须真的引用同一个 ArrayBuffer。下面的代码把字节数组作为消息载荷,转移其底层 buffer:
// main.js:把大块二进制输入交给 Worker,转移的是底层 buffer
const worker = new Worker("worker.js", { type: "module" });
const input = new Uint8Array([10, 20, 30, 40]);
worker.postMessage({
kind: "sum-bytes",
bytes: input.buffer, // 消息必须携带这个可转移资源
}, [input.buffer]); // 第二个参数交接 ArrayBuffer 的所有权
// 转移后发送端不再拥有这段内存,视图的 byteLength 会变为 0
console.log(input.byteLength); // 0
worker.addEventListener("message", (event) => {
// Worker 回传的是新的 ArrayBuffer,主线程重新建立视图
const output = new Uint8Array(event.data);
console.log(output[0]);
});
这里转移的是 input.buffer,不是 input。TypedArray 可以被结构化克隆,但它自身不能放进 transfer 列表;它只是一个带偏移量和长度信息的视图,真正拥有字节存储的是底层 buffer。

Worker 回传时要交接一个新的结果 buffer
如果 Worker 需要修改原始数据,可以直接在 Worker 一侧使用收到的 buffer,但回传给主线程时更容易维护的做法是创建输出数组。这样输入和输出各自有清晰的所有权,主线程也不会误以为原来的输入视图还能复用。
// worker.js:读取输入并创建独立结果,再把结果转回主线程
self.addEventListener("message", (event) => {
const input = new Uint8Array(event.data.bytes);
const output = new Uint8Array(input.length);
for (let i = 0; i
第二次转移后,Worker 中的 output 也不再可用;主线程收到消息后,event.data 才是新的 ArrayBuffer 所有者。不要在 postMessage 后继续读取或写入已经转出的视图,也不要把一个已经 detach 的 buffer 再次放进 transfer 列表。

结构化克隆、Transferable 和共享内存怎么选
三种方式解决的问题不同,不应只因为“数据量大”就盲目改成 Transferable:
| 方式 | 所有权 | 适合场景 | 注意点 |
|---|---|---|---|
| 结构化克隆 | 两边各有副本 | 配置、小对象、需要保留发送端数据 | 大块二进制可能产生复制成本 |
| Transferable | 一次只由一边拥有 | ArrayBuffer、MessagePort、ImageBitmap 等资源交接 | 发送端会 detach,需明确生命周期 |
| SharedArrayBuffer | 两边共享同一块内存 | 需要双方协作访问的高性能场景 | 同步、隔离与部署条件更复杂 |
如果主线程发送后还要继续使用原始字节,就选结构化克隆,或在发送前主动复制一份;如果任务是“交给 Worker 处理,处理完再交回来”,Transferable 的单向所有权模型更清楚。共享内存则要额外设计并发协调,不能把它当作 Transferable 的简单替代。
四个容易踩到的生命周期边界
- 消息和 transfer 不匹配:把 buffer 放入列表却不放进消息,接收端拿不到资源,而且发送端仍会失去它。
- 转移了视图而不是底层资源:对
Uint8Array使用 transfer 会抛出异常,应使用view.buffer,并留意视图的 offset 与 length。 - 回调外继续使用旧变量:主线程收到返回结果前,不能假设原输入仍有效;把后续逻辑放在
message回调或 Promise 封装内。 - 忽略异常与关闭:为
worker同时监听error和messageerror,任务完成且不再复用时调用terminate()。
相关问题
Transferable 会不会复制 ArrayBuffer?
对 ArrayBuffer 来说,重点是转移底层资源并让原对象 detach,而不是在两个线程各保留一份可写副本。具体资源的转移机制由对应接口定义。
为什么 postMessage 之后原来的 byteLength 变成 0?
这是所有权交接的可见结果。发送端不再拥有这段内存,因此应把它视为不可用状态,等待接收端回传新的 buffer 或重新创建输入。
-
130 收藏
-
414 收藏
-
163 收藏
-
137 收藏
-
160 收藏
-
282 收藏
-
175 收藏
-
410 收藏
-
400 收藏
-
文章 · 前端 | 7小时前 | websocket · 断线重连 · 前端排障 · 指数退避 · JavaScript异步 · WebSocket断线重连 WebSocket重连定时器 JavaScript指数退避 前端实时连接205 收藏
-
108 收藏
-
491 收藏
-
文章 · 前端 | 10小时前 | javascript · web components · custom elements · 前端排错 · Web Components Custom Elements attributeChangedCallback observedAttributes233 收藏
-
442 收藏
-
304 收藏
-
文章 · 前端 | 15小时前 | 前端 · View Transition API · 浏览器 API · 前端动画 View Transition API view-transition-name483 收藏
-
文章 · 前端 | 1天前 | 前端 · web components · 生命周期 · 自定义元素 · document Web Components Custom Elements adoptedCallback adoptNode importNode382 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习