# Fetch SDK

一个基于 Fetch API 的现代化 HTTP 客户端，提供简单易用的接口封装，支持请求拦截、流式数据和文件处理。

## 特性

- ✨ 优雅的 API 设计，类似 axios 的使用体验
- 🚀 支持请求和响应拦截器
- 📦 自动 JSON 数据处理
- 🔄 支持请求取消
- 📊 支持上传/下载进度监控
- 📥 支持流式数据处理
- 🛡️ 完善的 TypeScript 支持
- 🔌 支持多实例创建

## 快速开始

### 安装

```bash
npm install fetch-sdk
# 或
yarn add fetch-sdk
```

### 基础使用

```javascript
import fetchClient from 'fetch-sdk';

// 创建实例（推荐方式）
const service = fetchClient.create({
    baseURL: 'https://api.example.com',
    timeout: 5000,
    headers: {
        'Content-Type': 'application/json',
        'Accept': 'text/event-stream'  // SSE 支持
    },
    withCredentials: true,
    validateStatus: (status) => status >= 200 && status < 500,
    stream: true  // 启用流式处理
});

// 使用实例
try {
    const data = await service.get('/users', {
        params: { page: 1 },
        headers: { 'X-Token': 'xxx' }
    });
    console.log('请求成功:', data);
} catch (error) {
    console.error('请求失败:', error);
}
```

## 详细功能

### 1. 实例配置

创建实例时可配置的选项：

| 配置项 | 类型 | 默认值 | 说明 | 示例 |
|-------|------|-------|------|------|
| baseURL | string | '' | 请求的基础URL | `'https://api.example.com'` |
| timeout | number | - | 请求超时时间(ms) | `5000` |
| headers | object | `{'Content-Type': 'application/json'}` | 默认请求头 | `{ 'X-Token': 'xxx' }` |
| validateStatus | function | `status => status >= 200 && status < 300` | 状态码校验 | `status => status < 500` |
| withCredentials | boolean | false | 跨域请求是否带凭证 | `true` |
| responseType | string | 'auto' | 响应数据类型 | `'json'` |

```javascript
const service = fetchClient.create({
    baseURL: 'https://api.example.com',
    timeout: 5000,
    headers: {
        'Content-Type': 'application/json',
        'X-Custom-Header': 'value'
    },
    validateStatus: (status) => status < 500,
    withCredentials: true
});
```

### 2. 请求方法

支持的请求方法及其使用：

| 方法 | 参数 | 说明 | 示例 |
|------|------|------|------|
| get(url[, config]) | url: string, config?: RequestConfig | GET请求 | `service.get('/users', { params: { id: 1 } })` |
| post(url[, data[, config]]) | url: string, data?: any, config?: RequestConfig | POST请求 | `service.post('/users', { name: 'John' })` |
| put(url[, data[, config]]) | url: string, data?: any, config?: RequestConfig | PUT请求 | `service.put('/users/1', { name: 'John' })` |
| delete(url[, config]) | url: string, config?: RequestConfig | DELETE请求 | `service.delete('/users/1')` |
| request(url[, config]) | url: string, config?: RequestConfig | 通用请求方法 | `service.request('/api', { method: 'PATCH' })` |

```javascript
// GET 请求示例
const getExample = async () => {
    // 1. 简单请求
    const users = await service.get('/users');

    // 2. 带查询参数
    const user = await service.get('/users', {
        params: { 
            id: 1,
            type: 'detail'
        }
    });

    // 3. 带请求头
    const data = await service.get('/data', {
        headers: {
            'Authorization': 'Bearer token'
        }
    });
};

// POST 请求示例
const postExample = async () => {
    // 1. 发送 JSON 数据
    await service.post('/users', {
        name: 'John',
        age: 30
    });

    // 2. 发送表单数据
    const formData = new FormData();
    formData.append('file', file);
    await service.post('/upload', formData);

    // 3. 发送 URL 编码数据
    const params = new URLSearchParams();
    params.append('name', 'John');
    await service.post('/submit', params);
};
```

### 3. 拦截器

拦截器配置及使用：

```javascript
// 请求拦截器
service.interceptors.request.use(
    config => {
        // 请求前处理
        config.headers['Token'] = getToken();
        return config;
    },
    error => {
        // 请求错误处理
        return Promise.reject(error);
    }
);

// 响应拦截器
service.interceptors.response.use(
    response => {
        // 统一处理响应
        const { code, data, message } = response.data;
        if (code === 0) {
            return data;
        }
        throw new Error(message);
    },
    error => {
        // 错误处理
        if (error.response?.status === 401) {
            // 处理未授权
        }
        return Promise.reject(error);
    }
);
```

### 4. 文件处理

文件上传和下载功能：

#### 4.1 基础文件处理

```javascript
// 1. 文件上传
const uploadFile = async (file) => {
    const formData = new FormData();
    formData.append('file', file);
    
    try {
        await service.post('/upload', formData, {
            headers: {
                'Content-Type': 'multipart/form-data'
            },
            onProgress: ({ loaded, total, progress }) => {
                console.log(`上传进度: ${progress}%`);
            }
        });
    } catch (error) {
        console.error('上传失败:', error);
    }
};

// 2. 文件下载
const downloadFile = async () => {
    try {
        const blob = await service.download('/files/report.pdf', {
            filename: 'report.pdf',
            mimeType: 'application/pdf',
            onProgress: ({ progress }) => {
                console.log(`下载进度: ${progress}%`);
            }
        });
        
        // 如果不想自动下载，可以自行处理 blob
        return blob;
    } catch (error) {
        console.error('下载失败:', error);
    }
};
```

#### 4.2 断点续传

SDK 提供了文件上传和下载的断点续传功能，支持大文件传输时的断点恢复。

##### 上传断点续传

```javascript
// 断点续传上传示例
const handleUploadWithResume = async (file) => {
    try {
        await service.uploadWithResume(file, '/api/upload', {
            onProgress: ({ uploaded, total, progress }) => {
                console.log(`上传进度: ${progress}%`);
            }
        });
        console.log('上传完成');
    } catch (error) {
        console.error('上传暂停，已保存断点:', error.message);
        // 稍后可以使用相同的参数重新调用来继续上传
    }
};
```

##### 下载断点续传

```javascript
// 断点续传下载示例
const handleDownloadWithResume = async () => {
    try {
        const blob = await service.downloadWithResume('/api/files/large.zip', {
            filename: 'large.zip',
            mimeType: 'application/zip',
            onProgress: ({ downloaded, total, progress }) => {
                console.log(`下载进度: ${progress}%`);
            }
        });
        
        // 下载完成后处理文件
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = 'large.zip';
        a.click();
        URL.revokeObjectURL(url);
    } catch (error) {
        console.error('下载暂停，已保存断点:', error.message);
        // 稍后可以使用相同的参数重新调用来继续下载
    }
};
```

##### 特性说明

断点续传功能特点：

1. 自动分片：
   - 默认分片大小为 1MB
   - 可通过配置自定义分片大小
   - 支持超大文件传输

2. 进度保存：
   - 自动保存传输进度到 localStorage
   - 断点信息持久化
   - 支持页面刷新后继续传输

3. 错误处理：
   - 网络错误自动保存断点
   - 支持手动暂停/继续
   - 提供详细的错误信息

4. 进度监控：
   - 实时进度回调
   - 提供已传输大小和总大小信息
   - 支持进度百分比计算

##### 配置选项

| 选项 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| chunkSize | number | 1024 * 1024 | 分片大小（字节） |
| onProgress | function | - | 进度回调函数 |
| mimeType | string | 'application/octet-stream' | 文件类型 |
| filename | string | - | 保存的文件名 |

##### 服务端配置要求

服务端需要支持以下功能：

1. 分片上传接口：
```javascript
// 服务端接收分片示例（Node.js + Express）
app.post('/upload', (req, res) => {
    const uploadId = req.headers['x-upload-id'];
    const chunkIndex = req.headers['x-chunk-index'];
    const totalChunks = req.headers['x-total-chunks'];
    // 处理分片...
});
```

2. 断点下载支持：
```javascript
// 服务端支持断点下载示例
app.get('/download', (req, res) => {
    const range = req.headers.range;
    if (range) {
        // 处理断点下载请求...
        res.status(206);
        res.set('Accept-Ranges', 'bytes');
        // 发送部分内容...
    }
});
```

### 5. EventStream 响应处理

Server-Sent Events (SSE) 基本使用示例：

> **注意：** 使用流式处理（`stream: true`）时，`responseType` 设置将被忽略，因为流数据始终以文本形式返回。如果需要 JSON 数据，需要手动解析。

```javascript
const handleEventStream = async () => {
    const stream = await service.request('/events', {
        stream: true,
        headers: { 'Accept': 'text/event-stream' },
        // responseType: 'json', // 注意：此设置在流式处理时不生效
        method: 'POST',
        data: {
            message: 'Hello',
            type: 'chat'
        }
    });

    try {
        while (true) {
            const chunk = await stream.read();
            if (chunk === null) break; // 流结束
            
            // 需要手动解析 JSON 字符串
            try {
                const data = JSON.parse(chunk);
                console.log('收到 JSON 数据:', data);
            } catch (e) {
                console.log('收到普通文本:', chunk);
            }
        }
    } catch (error) {
        console.error('处理错误:', error);
    } finally {
        if (stream?.reader) {
            await stream.reader.cancel();
        }
    }
};

// 使用工具函数处理 JSON 流
const handleJSONStream = async () => {
    const stream = await service.request('/api/chat', {
        stream: true,
        method: 'POST',
        data: { message: 'Hello' }
    });

    const processJSON = (text) => {
        try {
            return JSON.parse(text);
        } catch (e) {
            return text;
        }
    };

    try {
        while (true) {
            const chunk = await stream.read();
            if (chunk === null) break;
            
            const data = processJSON(chunk);
            console.log('处理后的数据:', data);
        }
    } finally {
        if (stream?.reader) {
            await stream.reader.cancel();
        }
    }
};
```

更多高级用法请参考文档底部的 [高级特性 - EventStream](#高级特性) 章节。

### 跨域认证配置

跨域请求时的认证处理配置：

| 配置项 | 类型 | 默认值 | 说明 |
|-------|------|-------|------|
| withCredentials | boolean | false | 跨域请求时是否携带认证信息（cookies、HTTP认证及客户端SSL证书等）|
| credentials | string | 'same-origin' | 请求的凭据模式，可选值：'omit'、'same-origin'、'include' |

```javascript
// 1. 使用 withCredentials
const service = fetchClient.create({
    baseURL: 'https://api.example.com',
    withCredentials: true,  // 允许跨域请求携带 cookies
    // ...
});

// 2. 使用 credentials
const service = fetchClient.create({
    baseURL: 'https://api.example.com',
    credentials: 'include',  // 同 withCredentials: true
    // ...
});
```

注意事项：
1. 当设置 `withCredentials: true` 时：
   - 服务端必须设置 `Access-Control-Allow-Credentials: true`
   - 服务端的 `Access-Control-Allow-Origin` 不能设置为 '*'，必须指定具体域名
   - 响应头中的 `Set-Cookie` 才会被浏览器接受并存储

2. credentials 可选值说明：
   - `'omit'`: 从不发送 cookies
   - `'same-origin'`: 只有当请求同源时才发送 cookies（默认值）
   - `'include'`: 总是发送 cookies，等同于 `withCredentials: true`

3. 安全考虑：
   - 启用此配置会增加 CSRF 攻击风险，建议同时实现 CSRF 令牌机制
   - 仅在确实需要跨域认证时才启用此配置
   - 建议配合 HTTPS 使用，确保数据传输安全

4. 使用场景：
   - 跨域登录认证
   - 需要访问用户会话数据
   - 多服务之间的认证信息共享

```javascript
// 完整配置示例
const service = fetchClient.create({
    baseURL: 'https://api.example.com',
    withCredentials: true,
    headers: {
        'Content-Type': 'application/json',
        'X-Requested-With': 'XMLHttpRequest'  // 用于识别 AJAX 请求
    },
    validateStatus: (status) => status >= 200 && status < 500,
});

// 配合服务端设置示例（Node.js + Express）
app.use(cors({
    origin: 'http://localhost:8080',  // 指定允许的源
    credentials: true,  // 允许携带认证信息
    methods: ['GET', 'POST', 'PUT', 'DELETE'],
    allowedHeaders: ['Content-Type', 'X-Requested-With']
}));
```

## API 文档

### 请求方法
| 方法 | 参数 | 说明 | 示例 |
|------|------|------|------|
| get | (url, options?) | GET 请求 | `service.get('/users', { params: { page: 1 } })` |
| post | (url, data?, options?) | POST 请求 | `service.post('/users', { name: 'John' })` |
| request | (url, options?) | 自定义请求 | `service.request('/api', { method: 'PUT' })` |

### 请求配置
| 选项 | 类型 | 默认值 | 说明 | 示例 |
|------|------|--------|------|------|
| method | string | 'GET' | 请求方法 | `{ method: 'POST' }` |
| headers | object | `{'Content-Type': 'application/json'}` | 自定义请求头 | `{ headers: { 'X-Token': 'xxx' } }` |
| params | object | - | URL 查询参数 | `{ params: { id: 1 } }` |
| data | any | - | 请求体数据 | `{ data: { name: 'test' } }` |
| timeout | number | 30000 | 请求超时时间(ms) | `{ timeout: 5000 }` |
| responseType | string | 'auto' | 响应数据类型('json'/'text'/'blob'/'arrayBuffer'/'formData'/'auto') | `{ responseType: 'json' }` |
| signal | AbortSignal | - | 用于取消请求的信号 | `{ signal: controller.signal }` |
| stream | boolean | false | 以流式方式处理响应 | `{ stream: true }` |
| withCredentials | boolean | false | 是否携带凭证 | `{ withCredentials: true }` |
| validateStatus | function | status => status >= 200 && status < 300 | 响应状态码校验 | `{ validateStatus: status => status < 500 }` |

### stream 配置详解

stream 配置用于处理流式响应数据，主要用于以下场景：

1. Server-Sent Events (SSE) 接收实时更新
2. 大文件下载时分块处理
3. 流式 API 响应（如 ChatGPT 流式响应）

使用示例：

```javascript
// 1. 基础流式处理
const stream = await service.request('/stream-api', { 
    stream: true,
    headers: {
        'Accept': 'text/event-stream'
    }
});

// 使用 iterateLines 按行读取（适用于 SSE）
for await (const line of stream.iterateLines()) {
    if (line.startsWith('data: ')) {
        const data = JSON.parse(line.slice(6));
        console.log('收到数据:', data);
    }
}

// 2. 带进度监控的流式处理
const stream = await service.request('/large-file', { 
    stream: true,
    onProgress: ({ loaded, total, progress }) => {
        if (total) {
            console.log(`接收进度: ${progress}%`);
        } else {
            console.log(`已接收: ${loaded} bytes`);
        }
    }
});

// 使用 read 方法逐块读取数据
let chunk;
while ((chunk = await stream.read()) !== null) {
    console.log('收到数据块:', chunk);
}

// 3. 资源清理示例
let stream;
try {
    stream = await service.request('/stream', { stream: true });
    
    for await (const line of stream.iterateLines()) {
        console.log('接收到数据:', line);
    }
} finally {
    // 确保释放资源
    if (stream?.reader) {
        await stream.reader.cancel();
    }
}
```

流式响应对象的属性和方法：

| 属性/方法 | 类型 | 说明 |
|----------|------|------|
| read | async function | 读取下一个数据块，返回 null 表示数据已读取完毕 |
| iterateLines | async iterator | 按行迭代数据，适合处理文本流和 SSE |
| total | number | 总数据大小（仅当服务器提供 content-length 时可用）|
| loaded | number | 当前已接收的数据大小 |
| reader | ReadableStreamDefaultReader | 底层读取器实例，用于手动控制和资源清理 |

注意事项：

1. 使用 `read()` 方法时：
   - 返回 null 表示数据流结束
   - 返回的是解码后的文本数据
   - 自动更新 loaded 属性和触发进度回调

2. 使用 `iterateLines()` 时：
   - 自动处理行分割
   - 适合处理 SSE 和文本流
   - 会自动处理编码

3. 资源管理：
   - 务必在 finally 中调用 reader.cancel()
   - 避免资源泄露
   - 支持提前中断数据流

## 错误处理

SDK 使用标准化的错误对象,包含以下属性:

```javascript
try {
    await service.get('/api');
} catch (error) {
    // error.config - 请求配置信息
    // error.request - 请求实例
    // error.response - 响应对象(如果存在)
    // error.status - HTTP状态码(如果存在)
    // error.statusText - 状态描述(如果存在)
    console.log(error.message); // 错误消息
}
```

### 请求取消

使用标准的 AbortController 来取消请求：

```javascript
const controller = new AbortController();

service.get('/api/data', {
    signal: controller.signal 
}).catch(error => {
    if (error.name === 'AbortError') {
        console.log('请求已被取消');
    }
});

// 取消请求
controller.abort();
```

#### 实际应用场景

1. 搜索场景下取消上一次请求：
```javascript
let controller = null;

const handleSearch = async (keyword) => {
    // 取消之前的请求
    if (controller) {
        controller.abort();
    }
    
    // 创建新的 controller
    controller = new AbortController();
    
    try {
        const result = await service.get('/search', {
            params: { keyword },
            signal: controller.signal
        });
        return result;
    } catch (error) {
        if (error.name !== 'AbortError') {
            throw error;
        }
    }
};
```

2. 组件卸载时取消请求：
```javascript
import React, { useEffect } from 'react';

function DataComponent() {
    useEffect(() => {
        const controller = new AbortController();
        
        const fetchData = async () => {
            try {
                const data = await service.get('/api/data', {
                    signal: controller.signal
                });
                // 处理数据
            } catch (error) {
                if (error.name !== 'AbortError') {
                    console.error('获取数据失败:', error);
                }
            }
        };
        
        fetchData();
        
        // 组件卸载时取消请求
        return () => controller.abort();
    }, []);
    
    return <div>...</div>;
}
```

3. 超时和取消的结合：
```javascript
const timeoutRequest = async (url, timeout = 5000) => {
    const controller = new AbortController();
    
    try {
        const response = await service.get(url, {
            signal: controller.signal,
            timeout
        });
        return response;
    } catch (error) {
        if (error.name === 'AbortError') {
            throw new Error('请求超时或被取消');
        }
        throw error;
    }
};
```

## 最佳实践

### 请求取消示例
```javascript
// 1. 引入CancelToken
import fetchClient, { CancelToken } from 'fetch-sdk';
// 2. 创建AbortController
const controller = new AbortController();

// 3. 发起可取消请求
const getUserRequest = service.get('/users', {
  signal: controller.signal
}).catch(error => {
  if (error.name === 'AbortError') {
    console.log('请求已被取消');
  }
});

// 4. 取消请求
controller.abort();

// 5. 复用signal（可选）
const getPostsRequest = service.get('/posts', {
  signal: controller.signal // 使用同一个signal
});
```

1. 通用配置集中管理
```javascript
// api.js
const client = new FetchClient('https://api.example.com', {
    timeout: 5000,
    validateStatus: status => status < 500,
    headers: {
        'Accept': 'application/json',
        'X-Client-Version': '1.0.0'
    }
});

// 统一错误处理
client.addResponseInterceptor(
    response => response,
    error => {
        handleApiError(error);
        return Promise.reject(error);
    }
);

export default client;
```

2. 业务模块封装
```javascript
// userApi.js
import client from './api';

export const userApi = {
    getProfile: () => client.get('/user/profile'),
    updateProfile: (data) => client.post('/user/profile', data),
    uploadAvatar: (file) => {
        const formData = new FormData();
        formData.append('avatar', file);
        return client.post('/user/avatar', formData);
    }
};
```

3. 请求取消处理
```javascript
// 搜索场景
let searchCancel;

const search = async (keyword) => {
    // 取消上一次请求
    if (searchCancel) {
        searchCancel('新搜索请求发起');
    }
    
    const { token, cancel } = CancelToken.source();
    searchCancel = cancel;
    
    try {
        const result = await service.get('/search', {
            params: { keyword },
            cancelToken: token
        });
        return result;
    } catch (error) {
        if (!isCancel(error)) {
            throw error;
        }
    }
};
```

4. 流式数据优雅处理
```javascript
const handleStreamWithCleanup = async () => {
    let stream;
    try {
        stream = await service.request('/stream', { stream: true });
        for await (const line of stream.iterateLines()) {
            processLine(line);
        }
    } catch (error) {
        console.error('Stream error:', error);
    } finally {
        // 确保资源释放
        if (stream?.reader) {
            await stream.reader.cancel();
        }
    }
};
```

## 高级特性

### EventStream 详细说明

1. 作用说明：
   - 告知服务器客户端期望接收 Server-Sent Events (SSE) 格式的数据流
   - 服务器根据此头部判断是否使用 SSE 协议发送数据
   - 建立长连接，保持数据流的持续推送

2. 是否必需：
   - 不是强制必需的，但强烈建议设置
   - 不设置可能导致的问题：
     * 服务器可能返回普通 HTTP 响应而不是事件流
     * 某些服务器会拒绝不带正确 Accept 头的 SSE 请求
     * 无法享受浏览器对 SSE 的原生优化（如自动重连）

3. 使用场景：
```javascript
// 推荐的完整配置
const service = fetchClient.create({
    headers: {
        'Accept': 'text/event-stream',  // 声明接收 SSE
        'Cache-Control': 'no-cache',    // 禁用缓存
        'Connection': 'keep-alive'      // 保持连接
    }
});

// 最小配置（不推荐）
const service = fetchClient.create({
    // 不设置 Accept 头，依赖服务器默认行为
});
```

4. 服务器端对应配置：
```javascript
// Node.js Express 示例
app.get('/events', (req, res) => {
    // 检查客户端是否支持 SSE
    if (req.headers.accept && req.headers.accept.includes('text/event-stream')) {
        res.setHeader('Content-Type', 'text/event-stream');
        res.setHeader('Cache-Control', 'no-cache');
        res.setHeader('Connection', 'keep-alive');
        
        // 发送事件流
        const sendEvent = () => {
            res.write(`data: ${JSON.stringify({ time: new Date() })}\n\n`);
        };
        
        const timer = setInterval(sendEvent, 1000);
        
        req.on('close', () => clearInterval(timer));
    } else {
        // 客户端不支持 SSE，返回普通响应
        res.json({ error: 'SSE not supported' });
    }
});
```

5. 调试技巧：
   - 使用浏览器开发者工具查看 Network 面板
   - 确认请求头中包含 `Accept: text/event-stream`
   - 检查响应头中的 `Content-Type: text/event-stream`
   - 观察连接是否保持打开状态

6. 常见问题处理：
   ```javascript
   // 处理连接断开
   let retryCount = 0;
   const maxRetries = 3;
   
   const connectSSE = async () => {
       try {
           const stream = await service.request('/events', {
               stream: true,
               headers: {
                   'Accept': 'text/event-stream'
               }
           });
           
           retryCount = 0; // 重置重试计数
           
           for await (const line of stream.iterateLines()) {
               processEventData(line);
           }
       } catch (error) {
           if (retryCount < maxRetries) {
               retryCount++;
               console.log(`连接断开，${retryCount}秒后重试...`);
               setTimeout(connectSSE, retryCount * 1000);
           } else {
               console.error('多次重试失败，放弃连接');
           }
       }
   };
   ```

7. 性能考虑：
   - SSE 连接会占用服务器资源，建议设置最大连接数
   - 考虑在不需要时及时关闭连接
   - 可以使用心跳机制检测连接状态
   ```javascript
   // 心跳检测示例
   let lastEventTime = Date.now();
   const heartbeatInterval = setInterval(() => {
       const now = Date.now();
       if (now - lastEventTime > 30000) { // 30秒无数据
           controller.abort(); // 断开连接
           clearInterval(heartbeatInterval);
           connectSSE(); // 重新连接
       }
   }, 5000);
   ```

### 高级用法示例

#### 1. EventStream 高级处理

1.1 使用 iterateLines 处理 SSE：
```javascript
const handleSSEWithLines = async () => {
    const stream = await service.request('/events', {
        stream: true,
        headers: { 'Accept': 'text/event-stream' }
    });

    try {
        for await (const line of stream.iterateLines()) {
            if (line.startsWith('data: ')) {
                console.log('收到数据:', JSON.parse(line.slice(6)));
            }
        }
    } catch (error) {
        console.error('处理错误:', error);
    }
};
```

1.2 带缓冲的数据处理：
```javascript
const handleStreamWithBuffer = async () => {
    const stream = await service.request('/events', {
        stream: true,
        headers: { 'Accept': 'text/event-stream' }
    });

    try {
        let buffer = '';
        while (true) {
            const chunk = await stream.read();
            if (chunk === null) break;

            buffer += chunk;
            const lines = buffer.split('\n');
            buffer = lines.pop() || '';
            
            for (const line of lines) {
                if (line.startsWith('data: ')) {
                    console.log('处理数据:', JSON.parse(line.slice(6)));
                }
            }
        }
    } catch (error) {
        console.error('处理错误:', error);
    }
};
```

1.3 带超时和重试的事件流处理：
```javascript
const handleStreamWithTimeout = async () => {
    const stream = await service.request('/events', {
        stream: true,
        headers: { 'Accept': 'text/event-stream' }
    });

    try {
        while (true) {
            const timeoutPromise = new Promise((_, reject) => {
                setTimeout(() => reject(new Error('读取超时')), 5000);
            });

            const chunk = await Promise.race([
                stream.read(),
                timeoutPromise
            ]);

            if (chunk === null) break;
            console.log('收到数据:', chunk);
        }
    } catch (error) {
        if (error.message === '读取超时') {
            console.log('准备重试...');
        }
    }
};
```

#### 2. 请求重试机制
```javascript
const request = async (url, options = {}, retries = 3) => {
    for (let i = 0; i < retries; i++) {
        try {
            return await service.request(url, options);
        } catch (error) {
            if (i === retries - 1) throw error;
            await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
        }
    }
};
```

#### 3. 批量请求处理
```javascript
const batchRequest = async (urls, concurrency = 3) => {
    const results = [];
    const queue = [...urls];
    
    const workers = Array(concurrency).fill().map(async () => {
        while (queue.length) {
            const url = queue.shift();
            const result = await service.get(url);
            results.push(result);
        }
    });

    await Promise.all(workers);
    return results;
};
```

#### 4. 智能缓存
```javascript
const cacheMap = new Map();

const cachedRequest = async (url, options = {}, ttl = 60000) => {
    const key = `${url}-${JSON.stringify(options)}`;
    const cached = cacheMap.get(key);
    
    if (cached && Date.now() - cached.timestamp < ttl) {
        return cached.data;
    }

    const data = await service.request(url, options);
    cacheMap.set(key, { data, timestamp: Date.now() });
    return data;
};
```

#### 5. WebSocket 降级方案
```javascript
class RealTimeClient {
    constructor(url) {
        this.url = url;
        this.fallbackToSSE = false;
    }

    async connect() {
        try {
            if (!this.fallbackToSSE) {
                // 尝试 WebSocket
                this.ws = new WebSocket(this.url);
            } else {
                // 降级到 SSE
                const stream = await service.request(this.url, {
                    stream: true,
                    headers: { 'Accept': 'text/event-stream' }
                });
                this.handleSSE(stream);
            }
        } catch (error) {
            this.fallbackToSSE = true;
            this.connect();
        }
    }
}
```

#### 6. 文件上传断点续传
```javascript
const uploadWithResume = async (file, chunkSize = 1024 * 1024) => {
    const chunks = Math.ceil(file.size / chunkSize);
    let uploaded = 0;

    for (let i = 0; i < chunks; i++) {
        const chunk = file.slice(i * chunkSize, (i + 1) * chunkSize);
        const formData = new FormData();
        formData.append('chunk', chunk);
        formData.append('index', i);

        await service.post('/upload', formData, {
            headers: {
                'X-Upload-Id': uploadId,
                'X-Chunk-Index': i,
                'X-Total-Chunks': chunks
            }
        });
        
        uploaded += chunk.size;
        console.log(`上传进度: ${Math.round((uploaded / file.size) * 100)}%`);
    }
};
```
