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 + AbortControllerXMLHttpRequestEventSource
协议支持HTTP/1.1、HTTP/2、HTTP/3HTTP/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. 注意事项

  1. 资源释放

    • 使用reader.cancel() + reader.releaseLock() 确保释放流资源
    • 清除所有未完成的事件监听器
  2. 错误处理

    try {
      // 请求逻辑
    } catch (err) {
      if (err.name === 'AbortError') {
        console.log('用户主动终止');
      } else {
        console.error('请求异常:', err);
      }
    }
  3. 服务端影响

    • 中断仅客户端断开连接,服务端可能继续处理请求
    • 重要操作需配合服务端的终止机制
  4. 性能监控

    • 记录中断次数和时机
    • 监控未释放的资源(Memory Leak)
  5. 浏览器兼容性

    • 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}-`
  }
});