AbortController 是一个现代浏览器和Node.js提供的Web API,用于中止一个或多个Web请求。它不仅能用于取消fetch请求,还可以中止其他异步任务。下面我们来看看它的核心用法。
🧠 理解 AbortController 的核心
AbortController 本身设计得很简洁,主要包括:
| 组成部分 | 说明 |
|---|---|
abort() 方法 |
调用它就会触发中止信号。 |
signal 属性 |
这是一个AbortSignal对象,用于与DOM请求通信或中止DOM请求。 |
当你调用 controller.abort() 时:
- 会在
controller.signal上触发abort事件。 -
controller.signal.aborted属性的值会变为true。
任何关心中止操作的地方,都可以通过监听 signal 上的 abort 事件来响应。
🛠️ 基本用法与常见场景
1. 中止单个 Fetch 请求
这是最常见的用法,具体步骤如下:
-
创建控制器:
const controller = new AbortController(); -
传递 Signal:在调用
fetch时,将controller.signal作为选项中的signal属性传入。 -
中止请求:需要中止时,调用
controller.abort()。
当 fetch 请求被中止,其返回的 Promise 会被拒绝,并抛出一个名为 AbortError 的 DOMException。因此,务必使用 try...catch 来处理这个错误。
// 创建 AbortController 的实例
const controller = new AbortController();
// 假设有按钮触发中止
abortButton.addEventListener('click', () => {
// 点击按钮时中止请求
controller.abort();
});
try {
// 将 signal 传递给 fetch
const response = await fetch('https://api.example.com/data', {
signal: controller.signal
});
const data = await response.json();
// 处理数据...
} catch (error) {
// 判断错误是否为中止操作引起的
if (error.name === 'AbortError') {
console.log('请求已被中止');
} else {
console.error('其他错误:', error);
}
}
2. 在 React useEffect 中取消请求
在 React 函数组件中,这是一个非常有用的模式,可以避免组件卸载后尝试更新状态导致的错误。
import React, { useEffect, useState } from 'react';
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
// 1. 在Effect内部创建控制器
const controller = new AbortController();
const { signal } = controller;
// 2. 定义异步函数进行数据获取
const fetchData = async () => {
try {
const response = await fetch('/api/data', { signal });
const jsonData = await response.json();
setData(jsonData);
} catch (error) {
if (error.name !== 'AbortError') {
console.error('获取数据失败:', error);
}
}
};
fetchData();
// 3. 清理函数:当依赖变化或组件卸载时,中止请求
return () => {
controller.abort();
};
}, []); // 依赖数组
// 渲染...
}
3. 同时中止多个请求
同一个 AbortController 可以用于中止多个请求。只需将它的 signal 传递给每一个 fetch 调用即可。
// 创建一个控制器,可用于中止多个任务
const controller = new AbortController();
// 同时发起多个请求,并传递同一个 signal
const fetchPromises = urls.map(url =>
fetch(url, { signal: controller.signal })
);
// 当需要时,中止所有使用这个 signal 的请求
cancelButton.addEventListener('click', () => {
controller.abort();
});
try {
const responses = await Promise.all(fetchPromises);
// 处理所有响应...
} catch (error) {
if (error.name === 'AbortError') {
console.log('所有请求已被中止');
} else {
// 处理其他错误
}
}
4. 中止非 Fetch 的异步任务
AbortController 是一个通用工具,你也可以用它来中止任何异步任务。方法是监听 signal 的 abort 事件,并在事件触发时执行拒绝操作。
// 创建一个可以中止的 Promise 任务
function doAsyncTask({ signal }) {
return new Promise((resolve, reject) => {
// 如果信号已经中止,立即拒绝
if (signal.aborted) {
return reject(new DOMException('Aborted', 'AbortError'));
}
// 正常的异步操作,例如一个长时间运行的计算或定时器
const timer = setTimeout(() => {
resolve('任务完成!');
}, 5000);
// 监听中止事件,事件发生时清理并拒绝 Promise
signal.addEventListener('abort', () => {
clearTimeout(timer);
reject(new DOMException('Aborted', 'AbortError'));
});
});
}
// 使用
const controller = new AbortController();
const { signal } = controller;
// 在需要时中止任务
setTimeout(() => controller.abort(), 2000);
try {
const result = await doAsyncTask({ signal });
console.log(result);
} catch (error) {
if (error.name === 'AbortError') {
console.log('异步任务被中止');
}
}
5. 使用 AbortSignal 移除事件监听器
addEventListener 的选项参数中可以接收一个 signal。当这个信号被中止时,浏览器会自动移除该事件监听器。这是一种非常方便的管理事件监听的方式。
const controller = new AbortController();
const { signal } = controller;
// 添加事件监听器,并传入 signal
window.addEventListener('resize', () => {
// 处理 resize 事件
console.log('窗口大小改变了');
}, { signal }); // 注意这里的选项
// 在不需要时(例如组件卸载),移除所有通过这个 signal 注册的事件监听器
controller.abort();
💡 重要提示
-
abort()只能调用一次:一个AbortController实例一旦中止,就无法恢复。如果需要再次发起请求,必须创建一个新的AbortController实例。 -
处理
AbortError:捕获错误时,务必通过error.name === 'AbortError'来区分请求是被中止还是发生了其他网络错误。对于中止错误,通常不需要做特殊处理,静默处理即可。 -
this绑定:注意,在解构abort方法时,直接调用解构出来的方法可能会导致this指向丢失。安全起见,建议通过controller.abort()的方式调用,或者确保正确的this绑定。
下面这张流程图直观地展示了 AbortController 在请求中的工作流程和生命周期,特别是它在 React 组件中的典型使用方式:
flowchart TD
A[组件初始化] --> B[创建 AbortController]
B --> C[发起 Fetch 请求<br>传入 signal]
C --> D{请求进行中}
D --> E{用户主动取消或<br>组件卸载?}
E -- 是 --> F[调用 controller.abort]
F --> G[Fetch 请求被中止<br>Promise 被拒绝]
G --> H[捕获 AbortError<br>可选择静默处理]
E -- 否 --> I{请求成功?}
I -- 是 --> J[处理响应数据]
I -- 否<br>其他错误 --> K[处理其他错误]
H --> L[生命周期结束]
J --> L
K --> L
[图片上传失败...(image-787213-1764668935478)]