当前位置:首页 > 文章列表 > 文章 > 前端 > Web Worker 与主线程 Transferable 数据传递

Web Worker 与主线程 Transferable 数据传递

来源:17golang原创 2026-10-28 13:45:09 0浏览 收藏

Web Worker 传递二进制数据时,可以把 ArrayBuffer 放进 postMessage 的转移列表,把底层内存的所有权交给接收端。发送成功后,发送端原缓冲区会被分离,不能继续当作原数据使用。消息对象决定接收端拿到什么,转移列表决定其中哪些资源改变所有者。

规范入口:https://html.spec.whatwg.org/multipage/structured-data.html

从“发完以后数据没了”定位影响面

假设一个文件解析页面把字节交给 Worker,随后主线程还想用同一个视图生成预览,却发现长度变成零。这里是教学场景,并非一次真实线上事故。问题通常不在解析算法,而在原先默认“双方都有副本”的调用契约已经变成了“发送端交出资源”。

复盘可以分为三个观察点:创建视图时谁持有数据,发送时有没有指定 transfer,回调拿到的是新消息还是原变量。不要只凭界面是否卡顿判断传递方式,更不要把任务提交成功当成计算完成。

触发条件:视图、缓冲区与消息对象是不同层

Uint8Array 是查看字节的视图,通常可被结构化克隆,但它本身不是这里要放进转移列表的对象;可转移的是它引用的 buffer。只写 transfer 而没有把资源放进消息可达的数据中,接收端并不会因此获得一个业务字段。

// 正确:消息带上字节视图,转移列表指定其底层资源。
const bytes = new Uint8Array([10, 20, 30]);
worker.postMessage({ kind: "bytes", bytes }, [bytes.buffer]);
// 此后主线程不要再读写 bytes,把它视为已经交出的资源。

不传第二个参数时,该示例中的普通 ArrayBuffer 会随数据复制,发送端保留自己的内容。指定转移后,同一底层缓冲区关联的其他视图也受影响;改一个变量名不会获得独立副本。分离后的 ArrayBuffer 的 byteLength 为零,依赖该资源的操作可能返回空视图信息或抛出异常,不能把“每一种读取都抛错”当作通用判断。

消息载荷与缓冲区转移列表的静态关系
结构说明图:消息载荷中的字节视图引用底层缓冲区;转移列表指向同一资源。图示不是运行截图。

修复动作:用任务标识完成一次缓冲区往返

下面两个文件放在同一目录,以支持模块脚本的网页通过 HTTP 服务加载 main.js。页面提供按钮 id="send" 和文本容器 id="result"。示例只允许一个在途任务,Worker 对字节取反后将缓冲区归还。代码用于说明接口契约,本轮没有执行,也不提供性能测量结论。

main.js:

// 模块 Worker 的地址以当前脚本为基准;页面也应以模块方式加载本文件。
const worker = new Worker(new URL("./bytes-worker.js", import.meta.url), {
  type: "module"
});
const button = document.querySelector("#send");
const result = document.querySelector("#result");
let pending = null;
let sequence = 0;

function stopWithError(text) {
  // 故障后结束本示例会话,避免把已经交出的内存误当成可重试输入。
  worker.terminate();
  pending = null;
  button.disabled = true;
  result.textContent = text + ";重新加载页面后再试";
}
worker.addEventListener("error", () => stopWithError("Worker 执行失败"));
worker.addEventListener("messageerror", () => stopWithError("返回消息无法反序列化"));
worker.addEventListener("message", ({ data }) => {
  if (!pending || data.id !== pending.id) return;
  if (data.kind !== "done" || !(data.bytes instanceof Uint8Array)) {
    stopWithError(data.error || "返回协议不匹配");
    return;
  }
  // 使用返回消息里的视图;它不是主线程旧变量的重新激活。
  result.textContent = Array.from(data.bytes).join(", ");
  pending = null;
  button.disabled = false;
});
button.addEventListener("click", () => {
  if (pending) return;
  const id = ++sequence;
  const bytes = new Uint8Array([10, 20, 30]);
  pending = { id };
  button.disabled = true;
  try {
    worker.postMessage({ kind: "invert", id, bytes }, [bytes.buffer]);
    result.textContent = "等待 Worker 返回";
  } catch (error) {
    // 同步发送异常也要恢复按钮;下次点击会重新创建输入。
    pending = null;
    button.disabled = false;
    result.textContent = String(error);
  }
});
window.addEventListener("pagehide", () => {
  // 页面不再需要计算时释放 Worker。
  worker.terminate();
});

bytes-worker.js:

// Worker 内不访问 DOM;仅接收、处理并归还这一条二进制消息。
self.addEventListener("message", ({ data }) => {
  const { id, kind, bytes } = data;
  if (kind !== "invert" || !(bytes instanceof Uint8Array)) {
    self.postMessage({ kind: "error", id, error: "输入协议不匹配" });
    return;
  }
  try {
    for (let index = 0; index 

按示例运算规则,读者收到的应是各字节与 255 的差值;这是一项可自行检查的预期,不是本文记录的实测输出。真实业务还应增加超时、取消与页面恢复策略。超时或者 terminate 并不会自动把原缓冲区还给主线程;重试需要重新读取文件或另有原始数据来源。

根因扩展:子视图并不等于独立的小缓冲区

如果用 subarray 取出一小段数据,再转移它的 buffer,交出去的仍是整个底层 ArrayBuffer。同一缓冲区上的其他视图也失去原资源。如果只应发送选中片段,先复制该片段到独立视图,再转移新 buffer。

// subarray 只建立视图;slice 在这里为片段创建独立字节副本。
const original = new Uint8Array([1, 2, 3, 4, 5]);
const part = original.subarray(1, 4);
const ownedPart = part.slice();
worker.postMessage(
  { kind: "invert", id: 99, bytes: ownedPart },
  [ownedPart.buffer]
);
// original 仍持有自己的缓冲区;该片段复制成本是明确接受的代价。

这段是独立调用示意,不要直接追加到上面的单任务页面,否则任务标识 99 不在其 pending 协议中。也不要把同一个 buffer 因为对应多个视图就在转移列表里重复放入;重复的可转移对象会导致 DataCloneError。函数等不支持结构化克隆的数据混入消息,也可能在发送时触发异常。

共享底层缓冲区的子视图与独立副本关系
结构说明图:original 与 part 关联同一底层资源;ownedPart 来自片段复制,具有独立缓冲区。连线表示引用与来源关系,不表示运行时序。

防复发:把所有权写进任务协议

任务需求处理方式需要承担的代价
发送后主线程仍需原数据复制,或者保留独立副本复制时间与额外内存
大块二进制只由 Worker 使用转移 ArrayBuffer发送端放弃原资源
仅交出一个子范围slice 后转移新 buffer片段复制,不影响原缓冲区
Worker 处理后主线程需要结果将结果 buffer 再次转移回来以返回对象继续处理

任务协议至少明确任务标识、消息类型、资源当前所有者与失败后的重建来源。应用侧应限制在途任务数量,避免 Worker 计算赶不上连续提交,导致待处理数据持续积累。转移降低的是特定资源的复制负担,不会让算法成本、消息调度和排队自动消失。

为什么不能直接 transfer 一个 Uint8Array?

此处应 transfer 它的 ArrayBuffer,并把视图或缓冲区放在消息中。视图描述如何解释内存,底层资源才是本例改变所有权的对象。

转移回来会恢复原来的 JavaScript 对象吗?

不会。接收端应使用新消息里的对象;原来已经分离的对象不因往返而重新获得资源。

SharedArrayBuffer 也放进 transfer 列表吗?

不放。它属于共享内存机制,具有自己的部署条件与同步要求,不能用本例的独占转移契约替代。本文只围绕普通 ArrayBuffer 的 Worker 消息传递。

参考:MDN 可转移对象、Worker.postMessage 参数、HTML 结构化数据规范。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Fetch API ReadableStream 的增量 JSON 解析Fetch API ReadableStream 的增量 JSON 解析
上一篇
Fetch API ReadableStream 的增量 JSON 解析
IntersectionObserver trackVisibility 为什么需要 delay
下一篇
IntersectionObserver trackVisibility 为什么需要 delay
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    756次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    754次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    834次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    800次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    635次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码