如何通过 ReadableStream.tee() 将单路流完美克隆为两路独立的流进行双重消费

云磊大大_6114

云磊大大_6114

2026-05-19

672人浏览

原创

readablestream.tee() 是零拷贝分流而非克隆——它通过共享底层分发器同步推送同一chunk至两个独立分支,不复制内存,支持并发读取且互不干扰,但要求原始流未锁定且须在消费前调用。

如何通过 readablestream.tee() 将单路流完美克隆为两路独立的流进行双重消费

ReadableStream.tee() 并不是“克隆”,而是**零拷贝分流**——它不复制数据,只让两个新流共享原始流的读取逻辑,从而实现真正独立、并发、互不干扰的双重消费。

为什么 tee() 不是克隆,但效果更优

调用 tee() 后返回的是两个全新 ReadableStream 实例,它们:

  • 共用底层分发器,同一 chunk 会同步推送到两个分支,顺序完全一致
  • 不触发 Uint8Array 或 ArrayBuffer 的内存拷贝,无额外驻留开销
  • 各自可独立调用 getReader()、pipeTo() 或 for await...of
  • 任一分支 cancel() 或出错,不会中断另一分支(除非原始流本身终止)

必须满足的前提条件

tee() 能正常工作的硬性要求:

Upload audio to AIOZ Stream
Upload audio to AIOZ Stream

快速上传音频至 AIOZ Stream API。支持默认或自定义编码配置创建音频对象,上传文件并完成处理后返回音频链接。

下载
  • 原始流不能已被锁定:即不能已有活跃的 reader,也不能正在执行 pipeTo() 或 pipeThrough()
  • 必须在流开始消费前调用 —— 一旦 reader.read() 返回第一个 chunk,再调用 tee() 就会抛错
  • 调用 tee() 后,原始流即失效,后续所有操作只能通过两个分支流进行

典型双重消费场景写法

例如从 fetch 响应中同时记录日志并解析 JSON:

const response = await fetch('/api/data');
const [logStream, parseStream] = response.body.tee();

// 分支 A:记录传输量
logStream.pipeTo(new WritableStream({
  write(chunk) {
    console.log('已接收:', chunk.length, '字节');
  }
}));

// 分支 B:逐块解析 JSON 流(如使用 JSON.parseStreaming)
const reader = parseStream.getReader();
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  // 处理 value(Uint8Array),例如送入 JSON 解析器
}

常见误区与替代方案对比

不要用以下方式代替 tee():

  • response.body.clone():会缓存已读 chunk 并重放,存在内存堆积风险,且不支持字节流精确控制
  • 手动用 TransformStream + controller.enqueue() 复制:必然触发 ArrayBuffer 拷贝,失去零开销优势
  • 重复调用 fetch:引入额外网络请求和延迟,破坏流式语义

tee() 是目前唯一由标准规范定义、能兼顾无拷贝、低延迟、高并发的多路消费原生方案。

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.20

4746

5

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

2023.07.04

1189

3

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

2023.07.04

4664

6

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

2023.09.01

920

4

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.04

1836

5

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

2023.09.04

3385

4

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

2023.09.12

4373

6

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

2023.09.20

2860

5

java访问控制修饰符介绍
java访问控制修饰符介绍

java访问控制修饰符有四种,分别是public、protected、private、默认访问修饰符。详细介绍:1、public,public是最宽松的访问控制修饰符,被修饰的类、方法和变量可以被任何其他类访问,当一个类、方法或变量被声明为public时,它们可以在任何地方被访问,无论是同一个包中的类还是不同包中的类;2、protected修饰符等等。

2023.09.20

908

7

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
CentOS 官方文档
CentOS 官方文档

共0课时 | 0人学习

极客学院Java8新特性视频教程
极客学院Java8新特性视频教程

共17课时 | 4.3万人学习