前端处理流式数据

前端处理流式数据

前言

本篇文章为笔者开发过程中总结的关于前端处理后端流式接口返回的流式数据

示例

此处以仿gpt对话形式的发送消息函数为例,本次对话为一个包含提问和回答对象的数组

js 复制代码
async send() {

  // 生成新的对话框,并将其id赋值给Tid
  const Tid = this.messageIdCounter++

  // 复制Vue组件中的一些变量到本地变量
  // let _store = this.$store
  // 本次对话为一个包含提问和回答对象的数组
  let _Message = this.allMessages

  // 去除用户输入两端的空格(用作参数)
  this.userInput1 = this.userInput.trim()

  // 检查用户输入是否非空
  if (this.userInput.trim() !== '') {
    // 添加用户消息到对话中
    this.allMessages.push({
      id: this.messageIdCounter++,
      type: 'user',
      content: this.userInput.trim()
    });

    // 添加回答框到对话中
    this.allMessages.push({
      id: Tid,
      type: 'chatbot',
      content: ''
    });

    // 构建API请求的URL
    const url = '你的后端api'

    // 构建API请求的初始化参数
    const init = {
      method: 'post',
      headers: {
        'Content-Type': 'application/json',
        // 后端所需头
      },
      body: JSON.stringify({
        // 后端所需参数
      }),
    }

    try {
      // 发起异步网络请求
      const response = await fetch(url, init);

      // 检查网络响应是否成功
      if (!response.ok) {
        throw new Error(`HTTP error! Status: ${response.status}`);
      }

      // 获取响应体的数据读取器
      const reader = response.body.getReader();
      // 创建用于解码文本的解码器
      const decoder = new TextDecoder('utf-8');

      // 创建一个可读流
      const stream = new ReadableStream({
        async start(controller) {
          while (true) {
            // 从数据读取器中读取数据
            const { done, value } = await reader.read();

            // 如果读取完成,关闭控制器并退出循环
            if (done) {
              controller.close();
              break;
            }

            // 解码值并将其附加到指定ID的消息内容中
            const match = decoder.decode(value, { stream: true });
            if (match) {

			  // 此处以String拼接方式给对应的回答对象的内容拼接流式数据
              _Message[Tid].content = _Message[Tid].content + match + ''

              // 更新 Vuex 中的 streamingData 状态
              // _store.commit('updateStreamingData', dataValue);
            } else {
              // _store.commit('updateStreamingData', decoder.decode(value, { stream: true }));
            }
          }
        },
      });

      // 输出一条日志,表示流数据已接收完毕
      console.log('Full data');

    } catch (error) {
      // 捕获并打印任何网络请求错误
      console.error('Fetch error:', error);
    }

    // 清空用户输入
    this.userInput = '';
  }

  // 打印所有消息
  console.log(this.allMessages)
},

流程说明

  1. 生成新的对话框: 通过递增 messageIdCounter 变量,生成新的对话框的 ID,并将其赋值给 Tid 变量。

  2. 复制变量: 复制 Vue 组件中的一些变量到本地变量 _Message。

  3. 处理用户输入: 去除用户输入两端的空格,将处理后的用户输入保存在 userInput1 变量中。

  4. 检查用户输入非空: 如果用户输入不为空,执行以下操作:

  5. 添加用户消息: 向对话中添加用户输入的消息,包括消息的 ID、类型为 'user',以及去除两端空格后的内容。

  6. 添加聊天机器人回答框: 向对话中添加一个新的聊天机器人回答框,包括消息的 ID、类型为 'chatbot',初始内容为空。

  7. 构建API请求: 构建发送到后端的 API 请求的 URL 和初始化参数。

  8. 发起网络请求: 使用 fetch 函数发起异步网络请求,等待响应。

  9. 处理网络响应: 如果网络响应不成功,抛出错误。如果成功,获取响应体的数据读取器,用于处理流式数据。

  10. 处理流式数据: 创建一个可读流,循环读取数据,解码数据并将其附加到聊天机器人回答框的内容中。在这里,使用 String 拼接方式,将流式数据追加到聊天机器人回答框的内容。

  11. 输出日志: 输出一条日志,表示流数据已接收完毕。

  12. 捕获网络请求错误: 如果发生网络请求错误,捕获错误并输出错误日志。

  13. 清空用户输入: 无论是否成功,最终都清空用户输入。

  14. 打印所有消息: 输出当前所有对话框的消息内容,以便进行调试和查看结果。

注意

上方代码中提供了两种前端流式输出数据的方法,其中未被注释的是通过String拼接的方式;被注释掉的是通过更新vuex的方式

相关推荐
刘发财1 小时前
前端2秒生成500页矢量PDF,rust真的强到没朋友
前端·javascript·rust
郑州光合科技余经理2 小时前
国际版外卖系统:税率字段怎么和订单主流程解耦
android·java·开发语言·前端·后端·php·ai编程
梦想平凡2 小时前
百游棋牌源代码开发搭建教程(十):隔离部署、备份恢复与双端验收
java·前端·javascript·数据库·源代码管理
yume_sibai3 小时前
06-Rust Web 开发实战(Axum 框架 + 数据库 + JWT 认证 + 中间件 + 部署)
前端·数据库·rust
计算机魔术师5 小时前
特朗普上台打给黄仁勋:AI末日论是骗局,我们绝不让它发生
前端
troy1285 小时前
Python 基础语法(八):Web 后端开发、数据分析与可视化、网络爬虫、人工智能 / 大模型应用
前端·python·数据分析
计算机魔术师5 小时前
CEO说要慢下来,黑客说别做梦了——同一篇论文,两种命运
前端
kyriewen6 小时前
我花3天抓了一个幽灵bug,凶手藏在第4层
前端·javascript·程序员
IT_陈寒6 小时前
Java里用Stream.parallel()翻车实录,这性能还不如单线程
前端·人工智能·后端
wangchunyu1146 小时前
JavaScript 零基础入门教程
前端