当前代码实现了一个基于 @microsoft/fetch-event-source
的流式请求封装工具,主要特点包括:
- 提供了基本的流式请求功能
- 支持配置请求方法、头信息和请求体
- 提供了多个回调函数处理不同事件
- 实现了请求中止功能
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();