Web Streams 与增量文本解码

解释 ReadableStream 如何逐块提供字节,以及 TextDecoder 和文本缓冲区如何处理跨块字符与不完整记录。

#type / concept #status / evergreen #tech / dev / frontend #resource / javascript #platform / browser

[!info] related notes

Web Streams 与增量文本解码

一句话定义

Web Streams API 让消费者逐块读取响应字节;增量文本解码负责把任意分块的 Uint8Array 安全还原为字符和完整记录。

const reader = response.body?.getReader();
const decoder = new TextDecoder();
let buffer = '';

while (reader) {
  const { done, value } = await reader.read();
  if (done) break;
  buffer += decoder.decode(value, { stream: true });
}
buffer += decoder.decode(); // 冲刷解码器内部剩余字节

两种边界不能混淆

网络分块边界不等于:

  • UTF-8 字符边界
  • 文本换行边界
  • JSON 对象边界
  • SSE 事件边界

一个中文字符可能跨两个字节块,一行 JSON 也可能跨多个块。因此必须先增量解码,再用缓冲区寻找协议规定的完整记录。

stream: true

TextDecoder.decode(value, { stream: true }) 告诉解码器后面还有字节。若块末尾只有某个多字节字符的一部分,解码器会先保留它,等待下一块,而不是产生替换字符。

流结束时调用一次无参数 decoder.decode(),用于冲刷内部剩余状态。

缓冲区模式

buffer += decoded;
const lines = buffer.split('\n');
buffer = lines.pop() ?? '';
for (const line of lines) processLine(line);

最后一段可能是不完整行,必须留给下一次读取。协议如果允许 \r\n,处理行时还要规范化尾部的 \r

资源与错误边界

  • 响应 body 可能为 null
  • 读取可能因网络、取消或服务端断开而失败。
  • 不再读取时应释放 reader 或取消请求。
  • 测试应故意把 Unicode 字符和 JSON 行切在不同位置。
创建于 2026/7/29 更新于 2026/7/29