无需登录 数据私有 本地保存

可读流控制器 - 自定义ReadableStream

9
0
0
0

可读流控制器

可视化创建并操作自定义 ReadableStream,体验流式数据处理、背压机制与异步读取。

数据源配置 0
按回车键快速添加,修改后需重新创建流
暂无数据块,请添加
快捷预设:
流控制面板
等待创建
进度 0 / 0
ms
50-5000ms,模拟消费速度
📥 数据源 🔧 流管道 📤 消费者
desiredSize: — 已读: 0
输出日志
就绪 — 请先创建 ReadableStream

常见问题与知识点

ReadableStream 是 Web Streams API 的核心接口,用于异步、分块地读取数据。它解决了传统一次性加载大量数据导致的内存压力问题。典型场景包括:大文件分块上传/下载、网络响应流式处理、实时数据源(如传感器、WebSocket)的逐条消费。通过流式处理,数据无需全部加载到内存中即可开始处理,显著提升性能和用户体验。

ReadableStream 支持默认模式(Default mode)BYOB模式(Bring Your Own Buffer)。默认模式下,流自动分配缓冲区并返回数据块(Uint8Array 或其他类型);BYOB 模式下,消费者提供自己的缓冲区,流直接将数据写入其中,减少内存拷贝开销,适合高性能场景。本工具展示的是默认模式。

背压是指当数据生产速度快于消费速度时,系统自动减缓生产以防止内存溢出的机制。ReadableStream 内置背压支持:当内部队列已满(desiredSize ≤ 0)时,pull() 方法不会被调用,直到消费者读取了足够数据。在本工具中,设置较长的读取间隔即可模拟消费缓慢的场景,观察背压效果。

使用 new ReadableStream({ start(controller), pull(controller), cancel(reason) }) 构造函数:
  • start(controller):流初始化时调用,可在此设置初始状态。
  • pull(controller):消费者请求数据时调用,使用 controller.enqueue(chunk) 加入数据块,数据发完后调用 controller.close()
  • cancel(reason):消费者取消流时调用,用于清理资源。
本工具正是基于此模式,让您可视化地管理数据块并驱动流。

调用 stream.getReader()锁定该 ReadableStream,同一时间只能有一个活动 reader。这确保了数据不会被多个消费者交错读取。如果需要多个消费者同时读取,可使用 stream.tee() 方法将流分流为两个独立的副本。释放 reader 可调用 reader.releaseLock() 或等待流自然结束。

特性ReadableStream普通数组
数据加载按需异步拉取(惰性)一次性全部在内存中
内存占用可控,仅保留当前处理块全量占用
背压支持✅ 内置❌ 需手动实现
取消机制✅ cancel() 可中断❌ 遍历中难以中断
适用数据量无限/未知大小有限且已知

调用 reader.cancel(reason) 即可取消流。这会触发 ReadableStream 内部的 cancel() 回调,传入取消原因。取消后流进入 "cancelled" 终态,无法再读取。如果使用 for await...of 循环读取,取消操作会抛出错误,需用 try/catch 捕获。本工具提供了"取消流"按钮供您直观体验。

  • fetch 响应流式读取fetch(url).then(r => r.body.getReader()) 逐块处理大文件下载。
  • 文件分片上传:使用 File.stream() 将大文件分块上传,支持断点续传。
  • 实时数据推送:WebSocket / SSE 消息通过流逐条消费。
  • 视频/音频流:MediaStream 与 ReadableStream 结合处理音视频数据。
  • 数据转换管道:通过 pipeThrough() 串联 TransformStream 实现数据流式转换(如压缩、加密、JSON解析)。
  • Node.js 中stream.Readable 与 Web Streams API 互通,处理文件I/O、HTTP请求等。

const [branch1, branch2] = stream.tee() 将一个 ReadableStream 分流为两个完全独立的可读流。两个分支各自拥有独立的 reader,互不干扰。这在需要对同一数据源进行多重处理时非常有用,例如:一个分支用于实时显示,另一个分支用于保存到本地存储。注意,原流在 tee() 后会被锁定,需通过分支来读取。

for await...of 是更简洁的语法糖,自动循环读取直到流结束,代码可读性好,适合顺序处理所有数据。手动 read() 则提供更精细的控制:可以逐条决定是否继续读取、在读取间隙执行其他逻辑、灵活处理暂停/恢复。本工具同时支持两种方式——"自动读取"使用递归 read() 模拟 for await 行为,"读下一个"则是手动 read() 的直观体现。