💡前端入门: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时,要谨慎考虑是否真的需要取消请求,并尽量避免不必要的取消操作。
相关推荐
MediaTea17 分钟前
Jupyter Notebook:基于 Web 的交互式编程环境
前端·ide·人工智能·python·jupyter
少年阿闯~~18 分钟前
CSS——重排和重绘
前端
个人看法38 分钟前
h5实现一个吸附在键盘上的工具栏
前端·javascript·vue
知识分享小能手1 小时前
微信小程序入门学习教程,从入门到精通,微信小程序页面制作(2)
前端·javascript·学习·微信小程序·小程序·前端框架·notepad++
jason_yang1 小时前
JavaScript 风格指南 精选版
前端·javascript·代码规范
小高0072 小时前
🔍ECMAScript 2025 有哪些新特性?
前端·javascript
Hashan2 小时前
elpis-core:基于 Koa 的轻量级 Web 应用框架
前端·javascript·node.js
前端Hardy2 小时前
轻松搞定JavaScript数组方法,面试被问直接答!
前端·javascript·面试
云枫晖2 小时前
手写Promise-catch和finally
前端·javascript
薄雾晚晴2 小时前
大屏开发实战:封装自动判断、无缝衔接的文字滚动组件,告别文本截断烦恼
前端·javascript·vue.js