You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask返回生成器数据流 JavaScript读取流式响应异常排查

问题根因

代码无法实现逐秒打印的核心原因有两点:

  • 后端未配置流式响应头,服务端、反向代理层的缓冲机制会把逐秒生成的内容攒成整批后才发送给前端
  • 前端fetch用法有误:直接打印response对象只能拿到响应元信息,没有主动读取响应体的可读流,默认逻辑会等整个响应完全结束才返回完整内容,而你的接口是无限循环永不结束,自然拿不到分段输出的内容

修复方案

1. 后端Flask代码调整

需要给响应添加流式专属响应头,关闭缓冲,建议每次输出内容加固定分隔符(比如换行)方便前端切分,后续传JSON推荐直接用SSE(服务端推送事件)格式,解析更简单兼容性更好。

import time
import json
from flask import Flask, Response

app = Flask(__name__)

@app.route("/gen", methods=["GET"])
def gen():
    def make():
        while True:
            # 基础文本测试用这行
            yield "Hello\n"
            
            # 后续传JSON时改用SSE标准格式,注释掉上面的文本行用下面这段
            # msg = json.dumps({"content": "Hello", "timestamp": time.time()})
            # yield f"data: {msg}\n\n"
            
            time.sleep(1)
    
    # 流式响应必加请求头
    headers = {
        # 文本测试用text/plain,传SSE时替换为text/event-stream
        "Content-Type": "text/plain",
        "Cache-Control": "no-cache",
        "Connection": "keep-alive",
        "X-Accel-Buffering": "no" # 适配Nginx等反向代理,强制关闭代理缓冲
    }
    return Response(make(), headers=headers)

if __name__ == "__main__":
    # 开多线程避免单线程阻塞流式输出
    app.run(host="0.0.0.0", port=8001, threaded=True, debug=False)

2. 前端JavaScript代码调整

需要通过response.body.getReader()手动逐块读取流内容,同时加缓冲区处理TCP拆包/粘包问题,避免拿到不完整的内容片段。

<script>
const url = 'http://10.0.1.11:8001/gen';

fetch(url)
.then(response => {
    const reader = response.body.getReader();
    const decoder = new TextDecoder('utf-8');
    let buffer = ''; // 存储未处理完的不完整分块

    function readChunk() {
        return reader.read().then(({done, value}) => {
            if (done) {
                console.log("流传输结束");
                return;
            }

            // 二进制块转字符串拼入缓冲区
            buffer += decoder.decode(value, {stream: true});
            // 按换行切分完整消息
            const chunks = buffer.split('\n');
            // 最后一段可能是不完整内容,留在缓冲区等下次处理
            buffer = chunks.pop();

            chunks.forEach(chunk => {
                const content = chunk.trim();
                if (!content) return;
                
                // 文本测试直接打印
                console.log(content);

                // 处理SSE格式JSON时注释掉上面的打印,用下面这段
                // if (content.startsWith('data: ')) {
                //     try {
                //         const jsonData = JSON.parse(content.slice(6));
                //         console.log("收到JSON数据:", jsonData);
                //         // 此处写更新HTML页面的逻辑即可
                //     } catch (err) {
                //         console.error("JSON解析失败:", err);
                //     }
                // }
            })

            // 递归读取下一块内容
            return readChunk();
        })
    }

    return readChunk();
})
.catch(err => {
    console.error("流请求异常:", err);
})
</script>

注意事项
  • 本地测试如果出现跨域报错,在Flask接口里给响应头加上"Access-Control-Allow-Origin": "*"即可,生产环境按需配置允许的源
  • 不要在debug模式下测试流式接口,Flask调试重载器的缓冲机制会导致流输出异常
  • 纯服务端单向推流场景优先用SSE格式,不需要额外引入第三方库,浏览器原生支持自动重连,比手写裸流解析稳定性高很多;如果需要双向通信再考虑WebSocket

内容的提问来源于stack exchange,提问作者Chris

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 00:51:27