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();
相关推荐
Xy9102 分钟前
App Trace功能实战:一键拉起、快速安装与免提写邀请码的应用实践
前端
前端的日常2 分钟前
前端如何优化音频和视频的加载性能?
前端
是晓晓吖4 分钟前
在Next.js中,ISR是如何工作的?(译文)
前端
FogLetter4 分钟前
智能前端之拍照识别单词(下):AI集成与交互优化
前端·aigc·openai
yvvvy4 分钟前
# React Hooks 全面解析:从 useState 到 useEffect,掌握状态与副作用管理
javascript
满分观察网友z5 分钟前
告别平庸!我用 picker-view 造的这两个选择器,让产品经理闭嘴了
前端
jqq66611 分钟前
Vue3脚手架实现(七、渲染eslint配置)
前端·javascript·vue.js
Mintopia12 分钟前
BVH:光线追踪里的空间管家
前端·javascript·计算机图形学
Mintopia19 分钟前
Three.js 射线拾取原理:像素世界的侦探故事
前端·javascript·计算机图形学
掘金安东尼37 分钟前
前端周刊第421期(2025年7月1日–7月6日)
前端·面试·github