locked 是 ReadableStream 的只读属性,由内部状态机自动维护,true 表示存在活跃 reader;调用 getReader() 会锁流,releaseLock() 或 reader 自动释放后解锁;locked 为 true 时再调用 getReader() 会抛错,多消费者需用 tee() 分流。

可读流(ReadableStream)的 locked 状态不是由开发者手动设置的布尔标记,而是由流的内部状态机自动维护的一个只读属性,用于防止对同一流进行多路并发读取导致的数据竞争或逻辑错乱。
locked 属性的本质与判断方式
locked 是 ReadableStream 实例上的一个只读 getter 属性,返回 true 表示该流当前已被某个读取操作“占有”,即已调用 getReader() 获取了 reader,并且该 reader 尚未被释放(reader 未关闭、未出错、也未完成读取并被垃圾回收)。
- 只要存在一个活跃的
ReadableStreamDefaultReader或ReadableStreamBYOBReader,流就处于 locked 状态; - 即使 reader 调用了
read()但尚未 resolve(比如流暂停、底层源未推送数据),locked 仍为true; - reader 调用
releaseLock()后,locked 立即变为false; - reader 因流关闭或错误而自动释放后,locked 也会变为
false。
判断方式非常直接:
const stream = new ReadableStream({ /* ... */ });
console.log(stream.locked); // false
<p>const reader = stream.getReader();
console.log(stream.locked); // true</p><p>reader.releaseLock();
console.log(stream.locked); // falselocked 为 true 时的并发读取限制
当 stream.locked === true,任何进一步调用 stream.getReader() 都会立即抛出 TypeError: "Cannot get a reader when the stream is locked"。这是规范强制的行为,不是建议或警告。
- 它阻止了多个 reader 同时从同一个流拉取数据,避免“谁读到了哪段”不可控;
- 它确保了流控制(如背压、cancel、pipeTo)有唯一责任主体;
- 它不禁止“多处消费”,但要求显式分流:必须通过
tee()创建两个独立的、互不干扰的流分支。
如何安全实现多处读取
若需让多个消费者(如解析器 + 日志记录器 + 转码器)同时处理同一份流数据,不能复用 reader,而应使用 tee():
-
const [branch1, branch2] = stream.tee();生成两个完全独立的新流; - 每个分支都有自己的
locked状态,互不影响; - 原始流在
tee()后即被锁定(无法再getReader()),但两个分支可各自获取 reader; - 注意:tee 会缓冲已读但未消费的数据,内存开销随分支数和数据量上升。
常见误判与调试提示
不要通过是否能调用 getReader() 来“试探” locked —— 抛异常本身代价高;应始终先查 stream.locked。
- 忘记
releaseLock()是导致 locked 意外长期为 true 的主因; - 在 async 函数中未用
try/finally包裹 reader 使用,异常发生时 lock 无法释放; - 误以为
reader.closed为 true 就代表流已解锁 —— 实际上 reader 关闭后 lock 才释放,二者不同步。

















