前端逐字输出代码实现

一.fetch与axios的比较

fetchaxios 是两种常用的 HTTP 请求工具,在前端开发中经常使用来与后端进行通信。它们有一些重要的区别和各自的优缺点。其中流式处理在我们实现逐字输出中起到了主要作用

fetch

fetch 是现代浏览器内置的 API,用于发起网络请求。它返回的是一个 Promise,更加现代和简洁。

优点:
  1. 内置于浏览器:不需要额外的库文件,减少了依赖。

  2. 基于 Promise:更简洁和易于链式调用。

  3. 流式处理:支持 ReadableStream,方便处理大数据量或流式数据。

  4. 灵活性高:可以使用 RequestResponse 对象进行更多自定义操作。

缺点:
  1. 不自动处理 JSON:需要手动调用 response.json() 来处理 JSON 数据。

  2. 不支持取消请求:没有内置的取消请求机制(虽然可以使用 AbortController 进行额外处理)。

  3. 缺少一些常用功能:如超时设置、全局配置、拦截器等。

二. fetch的使用

使用步骤

  1. 发送请求

    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ question: question })
    });
    • 使用 fetch 发送 POST 请求到指定的 URL,设置请求头 Content-Typeapplication/json,并将 question 作为请求体发送。

  2. 检查响应体是否支持流

    if (!response.body) {
      throw new Error('ReadableStream not yet supported in this browser.');
    }
    • 检查 response.body 是否存在。如果不存在,抛出错误,表明浏览器不支持 ReadableStream

  3. 读取数据流

    jconst reader = response.body.getReader();
    let decoder = new TextDecoder('utf-8');
    • 获取读取器 response.body.getReader() 用于读取响应的流数据。

    • 创建一个 TextDecoder 实例,用于将二进制数据解码为字符串。

  4. 处理数据流

    while (true) {
      const { done, value } = await reader.read();
      if (done) break; // 如果数据流结束,退出循环
    ​
      const chunkData = decoder.decode(value, { stream: true }).trim();
      this.streams.push(chunkData);
      this.updateMessagesFromStreams();
    }
    • 使用 while (true) 循环持续读取数据流。

    • reader.read()

      返回一个 Promise,解析后得到一个对象

      { done, value }

      • done:如果为 true,表示数据流已结束,退出循环。

      • value:当前读取的二进制数据块。

    • 使用 decoder.decode 将二进制数据解码为字符串,并存入 streams 数组。

    • 每读取到一个数据块后,调用 updateMessagesFromStreams 方法来更新 messages 数组。

  5. 更新 messages 数组

    updateMessagesFromStreams() {
      const combinedMessage = this.streams.join('');
      this.messages = [{ content: combinedMessage, type: 'bot' }];
    }
    • updateMessagesFromStreams 方法将 streams 数组中的所有字符串连接成一个完整的字符串。

    • 更新 messages 数组,将连接后的字符串作为新的消息内容。

总结

通过使用 fetch 进行流式请求,可以有效地逐块处理数据并实时更新前端显示。与 axios 类似,fetchReadableStream 使得处理流数据更加简洁和现代化。此方法在数据量较大或需要逐步处理和显示数据的场景下尤为适用。

三. 实际代码实现

async sendToBackend(question) {
  const url = "http://localhost:5000/api/query"; // 确保这是正确的后端URL
  this.streams = [];
  this.sending = true;
​
  try {
    // 发送流式请求
    const response = await axios({
      method: 'post',
      url: url,
      data: { question: question },
      responseType: 'stream'
    });
​
    const reader = response.data.getReader();
    let decoder = new TextDecoder('utf-8');
​
    // 读取数据流
    while (true) {
      const { done, value } = await reader.read();
      if (done) break; // 如果数据流结束,退出循环
​
      // 假设数据是字符串形式的,直接存入 streams 数组
      const chunkData = decoder.decode(value, { stream: true }).trim(); // 假设数据是一个个汉字字符串
      this.streams.push(chunkData);
​
      // 更新 messages 数组(每获取到一个块就更新一次)
      this.updateMessagesFromStreams();
    }
​
    // 标记发送完成
    this.sending = false;
  } catch (error) {
    console.error('Error sending to backend:', error);
    this.sending = false;
  }
},
​
updateMessagesFromStreams() {
  // 将 streams 数组中的所有字符串连接成一个,并存入 messages 数组
  const combinedMessage = this.streams.join('');
  this.messages = [{ content: combinedMessage, type: 'bot' }];
}
​

  • 25
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值