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();
相关推荐
@PHARAOH32 分钟前
WHAT - 缓存命中 Cache Hit 和缓存未命中 Cache Miss
前端·缓存
Elastic 中国社区官方博客1 小时前
JavaScript 中使用 Elasticsearch 的正确方式,第一部分
大数据·开发语言·javascript·数据库·elasticsearch·搜索引擎·全文检索
万物得其道者成1 小时前
从零开始创建一个 Next.js 项目并实现一个 TodoList 示例
开发语言·javascript·ecmascript
海天胜景1 小时前
无法加载文件 E:\Program Files\nodejs\npm.ps1,因为在此系统上禁止运行脚本
前端·npm·node.js
MingT 明天你好!1 小时前
在vs code 中无法运行npm并报无法将“npm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查
前端·npm·node.js·visual studio code
老兵发新帖1 小时前
pnpm 与 npm 的核心区别
前端·npm·node.js
超级土豆粉1 小时前
怎么打包发布到npm?——从零到一的详细指南
前端·npm·node.js
OpenTiny社区1 小时前
TinyEngine 2.5版本正式发布:多选交互优化升级,页面预览支持热更新,性能持续跃升!
前端·低代码·开源·交互·opentiny
声声codeGrandMaster2 小时前
Django框架的前端部分使用Ajax请求一
前端·后端·python·ajax·django
sunbyte2 小时前
50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | Expanding Cards (展开式卡片)
javascript·vue.js·ecmascript