AI 流式请求工具函数 (通义千问)

当前代码实现了一个基于 @microsoft/fetch-event-source 的流式请求封装工具,主要特点包括:

  1. 提供了基本的流式请求功能
  2. 支持配置请求方法、头信息和请求体
  3. 提供了多个回调函数处理不同事件
  4. 实现了请求中止功能

1. 类型定义

js 复制代码
interface StreamFetchOptions {
  url: string;
  method?: string;
  headers?: Record<string, string>;
  body?: any;
  onMessage: (data: any) => void;
  onOpen?: (response: Response) => void;
  onClose?: () => void;
  onError?: (error: Error) => void;
}

interface StreamFetchResult {
  abort: () => void;
}

2. 错误处理

js 复制代码
onerror(err) {
  // 区分网络错误和服务器错误
  if (err instanceof Error) {
    console.error('Stream error:', err.message);
    onError?.(err);
  } else {
    const error = new Error(`Stream error: ${String(err)}`);
    console.error(error);
    onError?.(error);
  }
  throw err; // 保持原有行为
}

3. 数据解析

js 复制代码
onmessage(event) {
  try {
    if (event.data === '[DONE]') return;
    
    // 添加空数据检查
    if (!event.data || event.data.trim() === '') return;
    
    const data = JSON.parse(event.data);
    if (data) {
      onMessage(data);
    }
  } catch (err) {
    console.error('Error parsing stream data:', err);
    onError?.(err instanceof Error ? err : new Error(String(err)));
  }
}

5. 完整版本示例

js 复制代码
interface StreamFetchOptions {
  url: string;
  method?: string;
  headers?: Record<string, string>;
  body?: any;
  onMessage: (data: any) => void;
  onOpen?: (response: Response) => void;
  onClose?: () => void;
  onError?: (error: Error) => void;
  timeout?: number;
}

interface StreamFetchResult {
  abort: () => void;
}

export const streamFetch = ({
  url,
  method = 'POST',
  headers = {},
  body,
  onMessage,
  onOpen,
  onClose,
  onError,
  timeout = 30000
}: StreamFetchOptions): StreamFetchResult => {
  const controller = new AbortController();
  let timeoutId: NodeJS.Timeout;

  // 设置超时
  if (timeout > 0) {
    timeoutId = setTimeout(() => {
      controller.abort();
      const error = new Error(`Request timed out after ${timeout}ms`);
      onError?.(error);
    }, timeout);
  }

  fetchEventSource(url, {
    method,
    headers: {
      'Content-Type': 'application/json',
      ...headers
    },
    body: JSON.stringify(body),
    signal: controller.signal,
    openWhenHidden: true,
    async onopen(response) {
      if (timeoutId) clearTimeout(timeoutId);
      
      if (response.ok && response.headers.get('content-type')?.includes('text/event-stream')) {
        onOpen?.(response);
        return;
      }
      throw new Error(`Failed to connect: ${response.status} ${response.statusText}`);
    },
    onmessage(event) {
      try {
        if (event.data === '[DONE]') return;
        if (!event.data?.trim()) return;
        
        const data = JSON.parse(event.data);
        if (data) {
          onMessage(data);
        }
      } catch (err) {
        console.error('Error parsing stream data:', err);
        onError?.(err instanceof Error ? err : new Error(String(err)));
      }
    },
    onclose() {
      if (timeoutId) clearTimeout(timeoutId);
      onClose?.();
    },
    onerror(err) {
      if (timeoutId) clearTimeout(timeoutId);
      
      const error = err instanceof Error ? err : new Error(String(err));
      console.error('Stream error:', error.message);
      onError?.(error);
      throw err;
    }
  });

  return {
    abort: () => {
      if (timeoutId) clearTimeout(timeoutId);
      controller.abort();
    }
  };
};

基本使用

js 复制代码
import { streamFetch } from './streamFetch';

// 发起流式请求
const { abort } = streamFetch({
  url: 'url',
  method: 'POST',
  headers: {
    'Authorization': 'Bearer your-token'
  },
  body: {
    prompt: "你好,请介绍一下你自己",
    max_tokens: 1000
  },
  onMessage: (data) => {
    console.log('收到数据:', data);
  },
  onOpen: (response) => {
    console.log('连接已建立', response);
  },
  onClose: () => {
    console.log('连接已关闭');
  },
  onError: (error) => {
    console.error('发生错误:', error);
  }
});

// 如果需要中止请求
// abort();
相关推荐
s91236010110 分钟前
rust REGEX和lazy_static 和struct 混用
java·前端·javascript
vvilkim28 分钟前
React 与 Vue 虚拟 DOM 实现原理深度对比:从理论到实践
前端·vue.js·react.js
天天扭码32 分钟前
在项目中常见的main.js和main.mjs有什么区别,我们该如何选择?
前端·javascript·面试
姑苏洛言41 分钟前
在开发扫码小程序中,遇到胡坑“require() 默认不支持绝对路径”及其解决方案
前端
Passerby_K41 分钟前
vue3+dhtmlx 甘特图真是案例
前端·vue·甘特图
佳腾_1 小时前
【Web应用服务器_Tomcat】二、Tomcat 核心配置与集群搭建
java·前端·中间件·tomcat·web应用服务器
brzhang1 小时前
代码即图表:dbdiagram.io让数据库建模变得简单高效
前端·后端·架构
三巧1 小时前
纯CSS吃豆人(JS仅控制进度)
javascript·css·html
SummerGao.1 小时前
【解决】layui layer的提示框,弹出框一闪而过的问题
前端·layui
软件技术NINI2 小时前
html css js网页制作成品——HTML+CSS+js美甲店网页设计(5页)附源码
javascript·css·html