💡前端入门: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时,要谨慎考虑是否真的需要取消请求,并尽量避免不必要的取消操作。
相关推荐
凌辰揽月1 小时前
AJAX 学习
java·前端·javascript·学习·ajax·okhttp
然我3 小时前
防抖与节流:如何让频繁触发的函数 “慢下来”?
前端·javascript·html
鱼樱前端3 小时前
2025前端人一文看懂 Broadcast Channel API 通信指南
前端·vue.js
烛阴3 小时前
非空断言完全指南:解锁TypeScript/JavaScript的安全导航黑科技
前端·javascript
鱼樱前端3 小时前
2025前端人一文看懂 window.postMessage 通信
前端·vue.js
快乐点吧4 小时前
【前端】异步任务风控验证与轮询机制技术方案(通用笔记版)
前端·笔记
pe7er4 小时前
nuxtjs+git submodule的微前端有没有搞头
前端·设计模式·前端框架
七月的冰红茶4 小时前
【threejs】第一人称视角之八叉树碰撞检测
前端·threejs
爱掉发的小李4 小时前
前端开发中的输出问题
开发语言·前端·javascript
祝余呀5 小时前
HTML初学者第四天
前端·html