使用 fetch 获取 readablestream 后,通过 getreader() 读取 uint8array 块,用 textdecoder({stream: true}) 解码并缓冲截断字符,按换行符分块处理,实时渲染并滚动到底部,配合 try/catch 和 abortcontroller 实现健壮流式响应。

直接用 fetch 发起请求,再从 response.body 拿到 ReadableStream,手动读取、解码、分块处理——这是实现流式响应最可控也最常用的方式。它绕开了 response.text() 或 response.json() 这类“等全部到达才干活”的方法,让前端能真正边收边干。
拿到流并确认可用
发起请求后,先检查响应状态和 body 是否存在:
- 用
response.ok和response.status判断 HTTP 是否成功,避免静默失败 - 确保
response.body不为null(比如重定向或空响应可能没 body) - 服务端最好返回
Content-Type: text/plain、text/event-stream或application/json等明确类型,且不启用 gzip 压缩(或依赖浏览器自动解压)
用 getReader() 启动流读取
调用 response.body.getReader() 获取读取器,它返回一个 ReadableStreamDefaultReader:
-
reader.read()返回一个 Promise,resolve 结果是{ done, value } -
value是Uint8Array,即原始二进制块,不能直接当字符串用 -
done === true表示流已自然结束,应退出循环 - 推荐用
while(true)+await reader.read()控制流程,比递归更直观
解码二进制块并处理截断
流数据按网络包切分,UTF-8 多字节字符容易被截断在块边界:
- 创建
const decoder = new TextDecoder('utf-8'),显式指定编码 - 每次调用
decoder.decode(chunk, { stream: true }),stream: true表示保留未完成字符到下次解码 - 维护一个缓存字符串(如
let buffer = ''),把本次解码结果拼进去 - 再按
\n或其他分隔符切分出完整单元(如一行日志、一个 JSON 对象)
分块渲染与异常兜底
拿到完整数据单元后立即更新界面,而不是攒满再刷:
- 用
element.insertAdjacentHTML('beforeend', html)追加内容,避免全量重写 DOM - 滚动容器时调用
element.scrollTop = element.scrollHeight自动滚到底部 - 用
try/catch包裹reader.read(),捕获网络中断、解码错误等异常 - 流结束或出错时调用
reader.releaseLock(),释放读取锁(尤其取消时必须) - 配合
AbortController可随时中止整个 fetch 和后续流,信号需传入fetch({ signal })
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











