实现对deepseek流式返回的json数据,进行逐字解析并实时渲染

要实现对 流式返回的 JSON 数据(如 DeepSeek 的 SSE 流式响应)进行 逐字解析并实时渲染,而不是等待整个 JSON 完整加载后再处理,需要明确以下目标和问题:


核心目标

  • 逐步接收数据流(chunk by chunk)
  • 逐步拼接字符串
  • 逐步解析 JSON(部分解析)
  • 实时渲染/更新 UI

技术方案概述

1. 使用 fetch + ReadableStream 接收流式数据

const response = await fetch('https://api.deepseek.com/stream-endpoint', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    prompt: '讲一个故事'
  })
});

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

2. 读取 chunk 并拼接到 buffer 中

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  buffer += decoder.decode(value, { stream: true });

  // 尝试解析 JSON 或提取部分内容
  processBuffer(buffer);
}

关键难点:如何解析未完成的 JSON?

JSON 是结构化的文本格式,如果只收到一部分内容(比如 { "text": "Hello" } 只收到 { "text": "Hel),直接使用 JSON.parse() 会报错。

方法一:按行或分隔符切分 chunks

假设 DeepSeek 返回的是 多段 JSON 对象(每行一个完整 JSON),可以按 \n 分割:

function processBuffer(buffer) {
  const lines = buffer.split('\n');
  lines.forEach(line => {
    if (line.trim() === '') return;

    try {
      const json = JSON.parse(line);
      console.log(json.text); // 实时获取 text 字段
      updateUI(json.text);    // 实时渲染到页面
      buffer = '';             // 清空已处理部分
    } catch (e) {
      // JSON 不完整,保留 buffer 继续拼接
    }
  });
}

方法二:构建 JSON 增量解析器

如果你的数据是一个完整的 JSON(例如 {"content": "Hel..."} 但被分片传输),可以尝试使用增量解析库,如:

示例:

import sax from 'jsonparse';

const parser = new sax.Parser();

parser.onValue = function (value) {
  if (this.stack.length === 1 && this.key === 'content') {
    updateUI(value); // 实时渲染 content 内容
  }
};

function processBuffer(buffer) {
  try {
    parser.write(buffer);
  } catch (e) {
    // 忽略不完整 JSON 错误,继续等待更多数据
  }
}

实时渲染建议

你可以将每次解析出的内容追加到 DOM 中,模拟“打字效果”:

function updateUI(text) {
  const container = document.getElementById('output');
  container.textContent += text;
  container.scrollTop = container.scrollHeight; // 滚动到底部
}

示例:整合完整流程

async function startStreaming() {
  const response = await fetch('https://api.deepseek.com/stream-endpoint', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ prompt: '讲一个故事' })
  });

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

  parser.onValue = function (value) {
    if (this.stack.length === 1 && this.key === 'content') {
      updateUI(value);
    }
  };

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    buffer += decoder.decode(value, { stream: true });

    try {
      parser.write(buffer);
      buffer = ''; // 成功解析后清空 buffer
    } catch (e) {
      // JSON 不完整,保留 buffer 等待下一次拼接
    }
  }

  parser.close();
}

function updateUI(text) {
  const container = document.getElementById('output');
  container.textContent += text;
}

注意事项

项目建议
后端支持确保接口返回的是 text/event-stream 类型,并开启 CORS
错误处理添加网络中断、JSON 解析失败等异常捕获机制
性能优化避免频繁操作 DOM,可使用 requestAnimationFrame 或节流
安全性如果涉及用户输入,需做 XSS 过滤
完整性校验最终仍需在 done 后对最终结果做一次完整 JSON 解析
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

香蕉可乐荷包蛋

努力写有用的code

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值