💡前端入门: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时,要谨慎考虑是否真的需要取消请求,并尽量避免不必要的取消操作。
相关推荐
蓝瑟忧伤11 小时前
前端技术新十年:从工程体系到智能化开发的全景演进
前端
Baklib梅梅12 小时前
员工手册:保障运营一致性与提升组织效率的核心载体
前端·ruby on rails·前端框架·ruby
IT_陈寒12 小时前
Redis性能翻倍的5个冷门技巧,90%开发者都不知道第3个!
前端·人工智能·后端
jingling55514 小时前
vue | 在 Vue 3 项目中集成高德地图(AMap)
前端·javascript·vue.js
油丶酸萝卜别吃14 小时前
Vue3 中如何在 setup 语法糖下,通过 Layer 弹窗组件弹出自定义 Vue 组件?
前端·vue.js·arcgis
J***Q29220 小时前
Vue数据可视化
前端·vue.js·信息可视化
ttod_qzstudio1 天前
深入理解 Vue 3 的 h 函数:构建动态 UI 的利器
前端·vue.js
_大龄1 天前
前端解析excel
前端·excel
一叶茶1 天前
移动端平板打开的三种模式。
前端·javascript
前端大卫1 天前
一文搞懂 Webpack 分包:async、initial 与 all 的区别【附源码】
前端