Fetch处理流式数据需通过ReadableStream手动读取,仅当需边收边处理(如AI流式输出、实时日志)时才用getReader();常规接口用response.json()或text()更安全高效。

Fetch 处理流式数据的核心在于:响应体 response.body 是一个 ReadableStream,不能直接读取或解析,必须通过流读取器(getReader())分块消费,或用内置方法(如 text()、json())一次性获取完整内容。是否手动读流,取决于你是否需要“边收边处理”——比如实时日志滚动、AI 回复逐字渲染、大文件进度反馈。
判断是否真需要手动读流
多数普通接口(返回完整 JSON 或 HTML)用 response.json() 或 response.text() 就够了,语义清晰、自动解码、错误友好。只有当后端明确以流式方式持续推送数据(如 SSE、Coze/OpenAI 的 chunked JSON、自定义分块文本),且你希望在数据到达时立即响应(而非等全部接收完),才需手动读取流。
- ✅ 适合手动读流的场景:AI 对话流式输出、实时日志查看、大文件下载进度、SSE 事件监听(注意:SSE 更推荐用
EventSource) - ❌ 不必手动读流的场景:常规表单提交、列表分页加载、配置信息拉取
手动读取流的四步关键操作
手动控制流读取要确保逻辑完整、编码正确、资源释放及时:
-
获取读取器:
const reader = response.body.getReader()—— 必须在response.body未被消费前调用 -
逐块解码:每次
reader.read()返回{ done, value },其中value是Uint8Array;用new TextDecoder('utf-8')解码,避免 UTF-8 字节截断(如中文乱码) -
拼接与处理:流式数据常以换行分隔(如
data: {...}\n\n),需按协议清洗(例如提取data:后内容、JSON.parse 单条)、追加到 DOM 或响应式变量 -
释放锁并清理:循环结束后务必调用
reader.releaseLock();若支持中止(如用户点击“停止”),应配合AbortController提前退出循环
常见流数据格式与解析建议
不同后端返回的流格式差异较大,前端需对应处理:
立即学习“Java免费学习笔记(深入)”;
-
纯文本流(如日志):直接解码后按
\n分割,逐行渲染;注意过滤空行或末尾残留 -
JSON Lines(每行一个 JSON):对每块解码后的字符串用
split('\n'),再对非空行JSON.parse();需缓存跨块的不完整行 -
SSE 格式(
text/event-stream):虽可用 fetch + 手动读流模拟,但更稳妥的是用原生EventSource,它自动处理data:、event:、重连等逻辑 -
二进制流(如音频/模型权重):用
response.arrayBuffer()或结合reader.read()获取Uint8Array,再交给 WebAssembly 或 Canvas 处理
避坑提醒:流只能读一次
这是最容易忽略的关键限制:response.body 被任意方法消费后(哪怕只调了一次 response.text()),再次尝试 getReader() 或其他读取方法会报错:“Cannot read body after it has been consumed”。因此:
- 不要混用
response.json()和手动读流 - 调试时避免无意触发
console.log(response)—— 某些浏览器 DevTools 可能隐式调用读取方法 - 如需多处使用响应数据,应在首次读取后缓存结果(如存入变量或
Response.clone(),但 clone 仅适用于未开始读取的流)


















