主动中断 XHR(fetch/axios)请求和SSE(Server-Sent Events)请求(笔记)

396 阅读2分钟

1. fetch XHR 请求的主动中断

1.1 使用 AbortController

fetch 原生支持 AbortController 来中断请求。

示例代码

// 创建一个 AbortController 实例
const controller = new AbortController();
const signal = controller.signal;

// 发起 fetch 请求时传入 signal
fetch('https://jsonplaceholder.typicode.com/todos/1', { signal })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(err => {
    if (err.name === 'AbortError') {
      console.log('请求被中断');
    } else {
      console.error('请求出错', err);
    }
  });

// 需要中断请求时调用
controller.abort();

2. axios XHR 请求的主动中断

2.1 使用 CancelToken(axios v0.22及以下)

import axios from 'axios';

const CancelToken = axios.CancelToken;
let cancel;

// 发起请求
axios.get('https://jsonplaceholder.typicode.com/todos/1', {
  cancelToken: new CancelToken(function executor(c) {
    cancel = c;
  })
}).then(res => {
  console.log(res.data);
}).catch(thrown => {
  if (axios.isCancel(thrown)) {
    console.log('请求被取消');
  } else {
    console.error('请求出错', thrown);
  }
});

// 需要中断请求时调用
cancel('手动取消请求');

2.2 axios v1.0+ 推荐用 AbortController

const controller = new AbortController();

axios.get('https://jsonplaceholder.typicode.com/todos/1', {
  signal: controller.signal
}).then(res => {
  console.log(res.data);
}).catch(err => {
  if (err.code === 'ERR_CANCELED') {
    console.log('请求被取消');
  } else {
    console.error('请求出错', err);
  }
});

// 需要中断请求时调用
controller.abort();

3. SSE(Server-Sent Events)请求的主动中断

SSE 通常用 EventSource 或自定义 fetch+流的方式实现。

3.1 原生 EventSource

const es = new EventSource('/sse-url');
es.onmessage = (event) => {
  console.log('收到消息:', event.data);
};
// 需要中断时
es.close();

3.2 fetch+流(如你的 MyStream 实现)

你需要保存 AbortController 实例,并在需要时调用 abort()

伪代码示例

class MyStream {
  constructor() {
    this.controller = null;
    this.reader = null;
  }

  async getStream(url, onData, onEnd) {
    this.controller = new AbortController();
    const response = await fetch(url, { signal: this.controller.signal });
    const reader = response.body.getReader();
    this.reader = reader;

    // 读取流数据
    while (true) {
      const { value, done } = await reader.read();
      if (done) break;
      onData(value);
    }
    onEnd();
  }

  abortStream() {
    if (this.reader) this.reader.cancel();
    if (this.controller) this.controller.abort();
  }
}

// 使用
const stream = new MyStream();
stream.getStream('/sse-url', data => console.log(data), () => console.log('end'));
// 需要中断时
stream.abortStream();

4. 结合 Vue 组件的实际用法

data() {
  return {
    controller: null,
  }
},
methods: {
  fetchData() {
    this.controller = new AbortController();
    fetch('/api/data', { signal: this.controller.signal })
      .then(res => res.json())
      .then(data => { /* ... */ })
      .catch(err => {
        if (err.name === 'AbortError') {
          console.log('请求被中断');
        }
      });
  },
  abortFetch() {
    if (this.controller) this.controller.abort();
  }
}

总结

  • fetch/axios v1+ 推荐用 AbortController,调用 controller.abort() 主动中断。
  • axios v0.22-CancelToken
  • SSEEventSource.close() 或自定义流时用 AbortControllerreader.cancel()
  • 高频请求场景,一定要保存 controller/reader 实例,及时中断不再需要的请求,避免资源泄漏和页面卡死。

如需结合你项目的具体代码进一步优化,请补充说明!