readablestream.tee() 的核心价值在于零内存复制地实现数据分流——它不真正克隆字节,而是让两个新流共享同一底层数据源的读取逻辑,返回两个独立、可并发读取且互不影响的流分支。

ReadableStream.tee() 的核心价值在于**零内存复制地实现数据分流**——它不真正克隆字节,而是让两个新流共享同一底层数据源的读取逻辑。只要原始流尚未被消费,调用 tee() 就能立即获得两个独立、可并发读取的 ReadableStream 实例,每个都按需接收相同顺序的数据块。
tee() 分流的本质机制
tee() 返回的是两个逻辑上完全隔离的流分支,它们共用一个内部“分发器”,当原始流 push 数据时,该分发器自动将同一 chunk 同步推送给两个分支。这意味着:
- 没有 Uint8Array 或 ArrayBuffer 的深层拷贝,内存开销几乎为零
- 两个分支可各自调用 getReader()、pipeTo() 或 for-await-of,互不影响
- 任一分支提前 cancel 或出错,不会中断另一分支的读取(除非原始流本身终止)
典型多路分析场景示例
比如从 fetch 响应中同时做日志记录和内容解析:
- 分支 A:pipeTo(new WritableStream({ write: chunk => console.log('log:', chunk.length) })) 记录传输量
- 分支 B:使用 reader.read() 解析 JSON 片段或提取 HTML 标签
- 原始流 response.body 不需要额外构造,直接 tee() 即可启用双通道
关键使用约束与注意事项
tee() 虽轻量,但有明确前提:
- 必须在原始流未被锁定时调用(即不能已有活跃 reader 或处于 pipeTo/pipeThrough 过程中)
- 一旦 tee(),原始流即进入“不可直接读取”状态,后续所有操作必须通过分支流进行
- 若某一分支长时间不消费数据,可能造成背压累积,影响另一分支实时性(需合理控制 pull 策略)
替代方案对比:为什么不用 clone() 或手动复制?
clone() 方法虽也返回新流,但它会缓存已读 chunk 并在 clone 流中重放,存在内存驻留风险;而手动用 TransformStream + controller.enqueue 复制则必然触发 ArrayBuffer 拷贝。tee() 是唯一标准 API,能在规范层面保证无拷贝、低延迟、高并发的多路消费能力。











