1. 核心解决方案
1.1 Fetch API + AbortController(现代浏览器推荐)
// 创建中断控制器
const abortController = new AbortController();
// 发起请求
fetch('your-api-url', {
signal: abortController.signal
})
.then(response => {
const reader = response.body.getReader();
// 递归读取流数据
const read = () => {
reader.read().then(({ done, value }) => {
if (done) return;
console.log('Received:', value);
read(); // 持续读取
});
}
read();
})
.catch(err => {
if (err.name === 'AbortError') {
console.log('Request aborted');
}
});
// 中断操作
abortController.abort(); // 同时终止请求和流
1.2 XMLHttpRequest(兼容方案)
const xhr = new XMLHttpRequest();
xhr.open('GET', 'your-api-url');
// 流数据处理
xhr.onprogress = (event) => {
console.log('Received chunk:', xhr.responseText);
};
xhr.send();
// 中断操作
xhr.abort(); // 立即终止请求
1.3 EventSource(SSE场景)
const eventSource = new EventSource('your-api-url');
// 监听消息
eventSource.onmessage = (event) => {
console.log('SSE data:', event.data);
};
// 中断操作
eventSource.close(); // 关闭SSE连接
2. 方案对比
特性 | Fetch + AbortController | XMLHttpRequest | EventSource |
---|---|---|---|
协议支持 | HTTP/1.1、HTTP/2、HTTP/3 | HTTP/1.1 | 仅HTTP |
流控制 | 支持ReadableStream | 需手动处理 | 自动分块 |
错误处理 | 捕获AbortError | 监听abort事件 | 监听error事件 |
浏览器兼容 | IE除外(需polyfill) | 全兼容 | IE除外 |
中断后恢复 | 不可恢复 | 不可恢复 | 需重新建立连接 |
适用场景 | 现代Web应用 | 老式浏览器兼容 | 服务器推送场景 |
3. React最佳实践
3.1 组件卸载自动中断
import { useEffect } from 'react';
function StreamComponent() {
useEffect(() => {
const abortController = new AbortController();
const loadStream = async () => {
try {
const response = await fetch('/api/stream', {
signal: abortController.signal
});
const reader = response.body.getReader();
// ...处理流数据
} catch (err) {
if (err.name !== 'AbortError') {
console.error('Stream error:', err);
}
}
};
loadStream();
return () => abortController.abort(); // 清理函数
}, []);
return <div>Streaming Component</div>;
}
3.2 手动控制按钮
function ControlButton() {
const [abortController, setAbortController] = useState(null);
const startStream = () => {
const controller = new AbortController();
setAbortController(controller);
// ...启动流请求
};
const stopStream = () => {
if (abortController) {
abortController.abort();
setAbortController(null);
}
};
return (
<div>
<button onClick={startStream}>Start</button>
<button onClick={stopStream}>Stop</button>
</div>
);
}
4. 注意事项
-
资源释放
- 使用
reader.cancel()
+reader.releaseLock()
确保释放流资源 - 清除所有未完成的事件监听器
- 使用
-
错误处理
try { // 请求逻辑 } catch (err) { if (err.name === 'AbortError') { console.log('用户主动终止'); } else { console.error('请求异常:', err); } }
-
服务端影响
- 中断仅客户端断开连接,服务端可能继续处理请求
- 重要操作需配合服务端的终止机制
-
性能监控
- 记录中断次数和时机
- 监控未释放的资源(Memory Leak)
-
浏览器兼容性
- AbortController兼容性:Chrome 66+ / Firefox 57+ / Safari 12.1+
- 需要polyfill:
npm install abortcontroller-polyfill
5. 扩展知识
5.1 WebSocket中断
const ws = new WebSocket('wss://your-websocket');
// 中断连接
ws.close(1000, 'User initiated close'); // 状态码+原因
5.2 进度监控
// Fetch进度监控
const response = await fetch(url);
const total = +response.headers.get('Content-Length');
let received = 0;
const reader = response.body.getReader();
while(true) {
const {done, value} = await reader.read();
if (done) break;
received += value.length;
console.log(`Progress: ${(received/total*100).toFixed(1)}%`);
}
5.3 断点续传
// 设置Range头部
fetch(url, {
headers: {
'Range': `bytes=${resumePosition}-`
}
});