💡前端入门:AbortController 能中断哪些事件?一文搞懂!

凌晨你盯着监控大盘:用户点击"取消"后,页面还在拼命转圈,后台接口却像失控的火车,带着 200 MB 的文件流在公网上狂奔。 这是我们迫切想中断这个请求,但 Promise 内部也没给我们通过中断请求的方法

这个时候不径想到了角落里那个不起眼的 AbortController

一个 3 行代码就能"真·取消"任何 fetch/stream/setTimeout 的小工具, 今天,我们就把它从冷宫里捞出来,看看它到底怎么把异步任务从"失控"变成"可控"。

1、AbortController核心思想

  • 创建一个 AbortController 实例。

  • 实例获取一个 signal 属性。

  • 将这个 signal 传递给一个支持取消的异步操作。

  • 当你想取消这个操作时,调用实例abort() 方法。

1.1、实例 signal 属性

signal 是一个 AbortSignal 实例,可被用于任何支持中止的 API(如 fetch()、事件监听器等)

一旦 abort() 被调用,signal 就会触发 abort 事件,标记为已中止

1.2、实例 abort() 方法

  • 调用后会让该 signal 上监听的所有异步操作同时中止
js 复制代码
const controller = new AbortController();
window.addEventListener('resize', () => {
  console.log('Window resized!');
}, { signal: controller.signal });
// 调用 abort(),会自动移除 resize 监听器
document.getElementById('but').onclick = () => {
  controller.abort();
}

2、AbortController能中止哪些操作?

  1. Promise (通过手动检查信号状态)
  2. fetch() 请求
  3. XMLHttpRequest (XHR) 请求
  4. EventSource 连接
  5. WebSocket 连接 (部分实现)
  6. FileReader 操作
  7. Image 加载
  8. Js事件

2.1、fetch中断

fetch原生支持AbortController事件

js 复制代码
let controller = new AbortController();
fetch(url, {
  signal: controller.signal
}).catch((err)=>
         if (error.name === 'AbortError') {
         console.log('Abort中断');
         });
setTimeout(() => controller.abort(), 1000);//中断
  • 我们创建了一个 controller 和它的获取 signal

  • fetch 请求的 signal 选项被设置为 controller.signal

  • controller.abort() 被调用时,signal 会触发 abort 事件,fetch 请求会立即终止,并抛出一个 AbortError

  • catch 块中,我们可以通过 error.name === 'AbortError' 来区分是中止错误还是其他网络错误。

2.1.1、批量 fetch 中断

js 复制代码
let urls = [...]; // 要并行 fetch 的 url 列表

let controller = new AbortController();

// 一个 fetch promise 的数组
let fetchJobs = urls.map(url => fetch(url, {
  signal: controller.signal
}));

let results = await Promise.all(fetchJobs);

 controller.abort() 
// 它都将中止所有 fetch

2.2、中断Promise

当调用abort时,将在Promise内部改变Promise状态,达到中断Promise的目标

js 复制代码
// 1. 一个需要 5 秒才能完成的异步任务
function slowTask(signal) {
  return new Promise((resolve, reject) => {
    // 如果信号已经 abort,立即退出
    if (signal.aborted) {
      return reject(new Error('Aborted before start'));
    }
    const timer = setTimeout(() => {
      resolve('任务完成');
    }, 5000);
    // 监听 abort 事件
    signal.addEventListener('abort', () => {
      clearTimeout(timer);          // 清理资源
      reject(new Error('Aborted by user')); // 主动让 Promise rejected
    });
  });
}
// 2. 使用 AbortController
const controller = new AbortController();
const signal = controller.signal;
// 3. 启动任务
slowTask(signal)
  .then((res)=>consloe.log(res))
// 4. 2 秒后手动中断
setTimeout(() => {
  console.log('手动 abort');
  controller.abort();
}, 2000);

2.3、中断xhr

2.3.1、原生中断

xhr原生就支持中断,内部就存在一个abort函数可进行中断

js 复制代码
const xhr = new XMLHttpRequest();
const method = 'GET';
const url = 'https://xxx';
xhr.open(method, url, true);
xhr.onreadystatechange = () => {
  if (xhr.readyState === 4) {
  }
}
xhr.send();
setTimeout(()=>{
    xhr.abort()}
,1000)   

2.3.2、abortController中断

js 复制代码
const controller = new AbortController();
const signal = controller.signal;
signal.addEventListener('abort', () => {
  console.log('XHR request aborted!');
});
function loadXHRData() {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1');
 
  // 将 signal 绑定到 XHR
  xhr.signal = signal; // 注意:这是较新的 XHR 规范,旧浏览器可能不支持
  xhr.onload = function() {
    if (xhr.status >= 200 && xhr.status < 300) {
      console.log('XHR data received:', JSON.parse(xhr.responseText));
    } else {
      console.error('XHR error:', xhr.status, xhr.statusText);
    }
  };
  xhr.send();
}
loadXHRData();
setTimeout(() => {
  console.log('Attempting to abort XHR request...');
  controller.abort();
}, 100);

2.4、中断js事件

AbortController也能控制Js事件,包括dom事件。

在注册事件时,可以控制signal将直接将事件取消掉

js 复制代码
const controller = new AbortController();
window.addEventListener('resize', () => {
  console.log('Window resized!');
}, { signal: controller.signal });
// 调用 abort(),会自动移除 resize 监听器
document.getElementById('but').onclick = () => {
  controller.abort();
}

三、注意事项

  1. 及时清理资源
    当请求被取消后,确保及时清理与请求相关的资源,避免内存泄漏或其他潜在问题。
  2. 错误处理
    在处理fetch请求的Promise链时,要特别注意AbortError的处理。确保能够区分是因取消请求而引发的错误还是其他类型的错误,以便进行正确的错误处理。
  3. 多次调用abort
    abort方法可以被多次调用,但第二次及以后的调用不会有任何效果。一旦请求被取消,它将保持取消状态。
  4. 与其他API的兼容性
    虽然AbortController在现代浏览器中的支持已经相当广泛,但在一些较老的浏览器版本中可能还不支持。因此,在使用AbortController时,要注意检查目标浏览器的兼容性情况,并考虑使用Polyfill或备选方案来确保功能的可用性。
  5. 不要滥用
    虽然AbortController提供了取消请求的能力,但并不意味着我们应该滥用它。频繁地取消和重新发起请求可能会对服务器造成不必要的负担,也可能影响用户体验。因此,在使用AbortController时,要谨慎考虑是否真的需要取消请求,并尽量避免不必要的取消操作。
相关推荐
@大迁世界7 分钟前
TypeScript 的本质并非类型,而是信任
开发语言·前端·javascript·typescript·ecmascript
GIS之路16 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug19 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu1213821 分钟前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中43 分钟前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路1 小时前
GDAL 实现矢量合并
前端
hxjhnct1 小时前
React useContext的缺陷
前端·react.js·前端框架
前端 贾公子1 小时前
从入门到实践:前端 Monorepo 工程化实战(4)
前端
菩提小狗1 小时前
Sqlmap双击运行脚本,双击直接打开。
前端·笔记·安全·web安全
前端工作日常1 小时前
我学习到的AG-UI的概念
前端