如何实现XHR流式响应文本的逐块实时显示(类似ChatGPT)
流式文本逐块显示问题排查与修复
核心问题分析
你的代码逻辑本身没问题,但无法逐块输出通常是两个关键原因:
- 后端未启用分块传输编码,浏览器会等待完整响应接收后才触发读取事件
- 后端一次性输出所有内容,即使开启分块编码,也没有真正做到流式推送
修复后的前端代码
async read(url) { const response = await fetch(url, { method: "POST", headers: { "Content-Type": "text/plain", }, }); const data = response.body; if (!data) { return; } const reader = data.getReader(); const decoder = new TextDecoder(); let done = false; // 保留未完成解码的字节,处理多字节字符跨块的情况 let leftover = new Uint8Array(0); while (!done) { const {value, done: doneReading} = await reader.read(); done = doneReading; if (value) { // 合并剩余字节和当前块 const combined = new Uint8Array(leftover.length + value.length); combined.set(leftover); combined.set(value, leftover.length); try { const text = decoder.decode(combined, {stream: true}); console.log(text); leftover = new Uint8Array(0); } catch (e) { // 解码失败时,保留未完成的多字节字符部分 leftover = combined.slice(Number(e.message.match(/index (\d+)/)[1])); } } } // 处理最后剩余的字节 if (leftover.length > 0) { console.log(decoder.decode(leftover)); } }
后端必须配置的关键点
- 强制设置响应头:
Transfer-Encoding: chunked,明确告知浏览器这是分块响应 - 后端要逐块输出内容并主动刷新缓冲区:比如Node.js中用
res.write()替代res.end(),每次写入后调用res.flush();Java Servlet中调用response.flushBuffer() - 禁用会缓冲输出的中间件:部分压缩、缓存中间件会合并分块,导致流式推送失效
内容的提问来源于stack exchange,提问作者Sylvain
相关产品推荐
相关产品推荐

