AbortController
是 JavaScript 中的一个全局类,主要用于中止正在进行或待完成的异步操作,如 fetch
请求、事件监听、可写流、数据库事务等。通过 AbortController
,我们可以在需要时自由地终止这些操作,避免不必要的开销或冗长的等待。
基本用法:
- 创建
AbortController
实例 :通过new AbortController()
创建一个控制器实例。 - 获取
signal
对象 :通过controller.signal
获取一个AbortSignal
对象,该对象可以传递给需要中止的 API。 - 中止操作 :调用
controller.abort()
方法会触发与该控制器关联的所有操作的中止。
使用实例:
fetch
使用AbortController
const controller = null;
const fetchApply = async () => {
controller = new AbortController();
const signal = controller?.signal;
try {
let params = {};
const response = await fetch("http://127.0.0.1", {
method: "POST",
headers: {
"Content-Type": "application/json", // 设置请求头,表明发送的内容是 JSON
},
body: JSON.stringify(params),
signal: signal,
});
if (!response.ok) {
throw new Error("网络响应不正常");
}
} catch (err) {
if (err.name === "AbortError") {
console.log("AbortError-请求被取消");
} else {
console.log(err.message, "err.message");
}
}
};
axios
使用AbortController
const axiosApply = async () => {
controller = new AbortController();
const signal = controller?.signal;
try {
let params = {};
const response = await axios({
url: "http://127.0.0.1",
method: "POST",
headers: {
"Content-Type": "application/json", // 设置请求头,表明发送的内容是 JSON
},
data: params,
signal,
});
if (!response.ok) {
throw new Error("网络响应不正常");
}
} catch (err) {
if (err.name === "CanceledError") {
console.log("AbortError-请求被取消");
} else {
console.log(err.message, "err.message");
}
}
};
- 取消请求
const cancelRequest = () => {
if (controller) {
controller?.abort(); // 取消请求
}
};
- 基于
AbortController
构建轮询函数
/**
* 轮询函数(支持取消、指数退避、错误处理)
* @param {*} params 请求参数
* @param {*} options 处理参数
* @returns
*/
const polling = async (params, options = {}) => {
const {
maxRetries = 10, // 最大重试次数
baseDelay = 1000, // 基础延迟(毫秒)
maxDelay = 30000, // 最大延迟(毫秒)
signal = null, // 取消信号(AbortController)
} = options;
let retryAttempts = 0; // 错误重试计数器
let pollingAttempts = 0; // 轮询尝试计数器
let lastError = null;
const calculateDelay = attempt => {
return Math.min(baseDelay * 2 ** attempt, maxDelay);
};
// 1. 检查是否已取消
const waitWithAbort = delay => {
return new Promise((resolve, reject) => {
const timer = setTimeout(resolve, delay);
// 支持延迟期间取消
if (signal) {
signal.addEventListener(
"abort",
() => {
clearTimeout(timer);
reject(new Error("轮询已取消"));
},
{ once: true }
);
}
});
};
while (true) {
try {
if (signal?.aborted) {
throw new Error("轮询已取消");
}
// 2. 发起查询请求
const { code, data, msg } = await queryAsrTask(params, signal);
pollingAttempts++;
// 3. 处理响应逻辑
if (code === 200) {
if (data.is_finish) {
return data.result; // 任务完成,返回结果
} else {
// 任务未完成时使用独立延迟计算
const delay = calculateDelay(pollingAttempts);
await waitWithAbort(delay);
}
} else {
// 非200响应,抛出错误终止轮询
throw new Error(msg || "后端返回未知错误");
}
} catch (error) {
// 4. 统一错误处理
if (
error.name === "AbortError" ||
error.code === 400 || // 示例:客户端错误不重试
retryAttempts >= maxRetries
) {
if (!lastError) {
console.log("错误", error.message);
}
throw error;
}
// 记录错误并应用退避
lastError = error;
retryAttempts++;
// 指数退避 + 抖动 (jitter)
const delay = calculateDelay(retryAttempts);
const jitter = delay * 0.2 * Math.random();
await waitWithAbort(delay + jitter);
}
}
};
// 使用示例
const controller = new AbortController();
// 启动轮询
polling(params, { maxRetries: 8, baseDelay: 1500, signal: controller.signal })
.then(data => console.log("任务完成:", data))
.catch(error => {
if (error.message.includes("取消")) {
console.log("用户主动取消了轮询");
} else {
console.error("任务失败:", error);
}
});
// 取消轮询
// 用户点击取消按钮时调用
document.getElementById("cancel-button").addEventListener("click", () => {
controller.abort();
});
在 JavaScript 中,任何被声明为
async
的函数都会自动返回一个Promise
。这是async
函数的一个特性,无论函数内部是否使用了await
,只要函数被声明为async
,它的返回值就会被自动包装在一个Promise
中。