fetchEvent流式响应vue3

window中有一个叫EventSource的构造函数。一个EventSource实例会对服务器开启一个持久化的连接,以text/event-stream格式发送事件,此连接会一直保持开启直到通过调用EventSource.close()关闭。但使用EventSource时只能把参数加到url后面,而且也不能像fetch请求那样设置header等参数。借助fetch-event-source这个库就可以像发起fetch请求一样发起服务器单向通信请求。

对接AI的api,返回的数据是流式的,后台不好转,前端项目vue3
使用 @microsoft/fetch-event-source,实现fetchEvent 的post传送

fetch-event-source使用

 await fetchEventSource(url, {
        method: "POST",
        headers: {
          "Content-Type": "application/json; charset=utf-8",
          "Accept": "text/event-stream",
          "Authorization": `Bearer XXXX`,
          "Transfer-Encoding": "chunked",
        },
        body: JSON.stringify(params),
        signal: cancelTokenSourceRef.current.signal,
        onopen(response) {  // 成功建立连接
          },
        onmessage(msg) { // 接收信息
			// 这里接收数据处理业务逻辑
			 const result = JSON.parse(msg.data);
			 if (result.event === "message") {
}else if (result.event === "message_end"){
console.log("回答完毕");}
},  // 接收信息
        onerror(err) {
          console.error("发生错误", err);
          throw err;
        },
        onclose(res) {
          console.log("关闭链接", res);
        },
        openWhenHidden: true,
      });

接下来直接展示代码吧

  1. 下载 @microsoft/fetch-event-source
npm install @microsoft/fetch-event-source

  1. 项目引用
import { fetchEventSource } from "@microsoft/fetch-event-source";
  1. 中断会话的用处
// 用于中断会话,中断时调用cancelTokenSourceRef.current.abort();
const cancelTokenSourceRef = ref(new AbortController());
cancelTokenSourceRef.current = new AbortController();
  1. 设置重试次数,有时候是网络原因,所以要再次请求看看
  let retryDelay = 1000; // 初始重试延迟(毫秒)
  const maxRetryDelay = 30000; // 最大重试延迟(毫秒)
  const maxRetries = 3; // 最大重试次数

完整代码

const cancelTokenSourceRef = ref(new AbortController());
cancelTokenSourceRef.current = new AbortController();

const attemptFetch = async (retryCount = 0) => {
    try {
       var params = {
        inputs: {},
        query:  text || prompt.value,
        response_mode: "streaming",
        conversation_id: conversation_id.value, // 选填)会话 ID
        user: loginUser.value.nickname || "abc-123", // 用户标识
        auto_generate_name:true //  选填)自动生成标题
      };
      // console.log('difyConfig------------params',params);
      await fetchEventSource(`/difyChatApi/v1${difyConfig.value.path}`, {
        method: "POST",
        headers: {
          // "Content-Type": "application/json",
          "Content-Type": "application/json; charset=utf-8",
          "Accept": "text/event-stream",
          "Authorization": `Bearer ${difyConfig.value.apikey}`,
          "Transfer-Encoding": "chunked",
        },
        body: JSON.stringify(params),
        signal: cancelTokenSourceRef.current.signal,
        onopen(response) {
          if (response.ok) {
            console.log("成功建立连接");
            
            connect_success = false;
            // 重新链接的时候 char重置为0
            typeWriterObj.charIndex = 0;
            typeWriterObj.timer = null;
            typeWriterObj.flag = false;
            retryDelay = 1000; // 重置重试延迟
          } else {
            chatData.value[chatData.value.length - 1].isLoading = false;
            chatData.value[chatData.value.length - 1].content = "连接失败。ε(┬┬﹏┬┬)3";
            enableInput();
            throw new Error("连接失败");
          }
        },
        onmessage(msg) {
          
          console.log('Received message:');
          if (msg.data) {
            // connect_success来判断是否是第一次的信息加载
            if (!connect_success) {
              console.log('接收消息---onmessage');              
              connect_success = true;
            }
            const result = JSON.parse(msg.data);
            if (result.event === "message") {
              conversation_id.value = result.conversation_id;
              session_id.value = result.message_id;
              console.log("流");
            } else if (result.event === "message_end") {

              // 回复
              console.log("回答完毕");
            }
            // 将聊天框的滚动条滑动到最底部
            nextTick(() => {
              document
                .getElementById("chat-box")
                .scrollTo(0, document.getElementById("chat-box").scrollHeight);
            });
          }
        },
        onerror(err) {
          console.error("发生错误", err);
          throw err;
        },
        onclose(res) {
          console.log("关闭链接", res);
          // lineBuffer.value = ""
          // disableInput(true);
        },
        openWhenHidden: true,
      });
    } catch (err) {
      console.log("错误是什么?", err);
      if (retryCount < maxRetries) {
        console.log(`尝试重连... (重试次数: ${retryCount + 1})`);
        retryTime.value =  setTimeout(() => {
          attemptFetch(retryCount + 1);
        }, retryDelay);
        retryDelay = Math.min(retryDelay * 2, maxRetryDelay); // 指数退避
      } else {
        console.error("达到最大重试次数,停止重试");
      }
    }
  };

  attemptFetch();
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值