AbortController
AbortController()
构造函数创建了一个新的 AbortController
实例。MDN官网给出了一个利用AbortController取消下载视频的例子。
核心逻辑是:利用AbortController接口的只读属性signal标记fetch请求;然后在需要取消请求的时候,调用AbortController接口的abort()方法立即取消请求,并抛出一个错误AbortError。
javascript
const controller = new AbortController();
const signal = controller.signal;
const url = "video.mp4";
const downloadBtn = document.querySelector(".download");
const abortBtn = document.querySelector(".abort");
downloadBtn.addEventListener("click", fetchVideo);
abortBtn.addEventListener("click", () => {
controller.abort();
console.log("Download aborted");
});
function fetchVideo() {
fetch(url, { signal })
.then((response) => {
console.log("Download complete", response);
})
.catch((err) => {
console.error(`Download error: ${err.message}`);
});
}
只读属性signal
AbortController接口的只读属性 signal
返回一个 AbortSignal 实例对象,该对象可以根据需要处理 DOM 请求通信,既可以建立通信,也可以终止通信。
方法:abort()
AbortController接口的 abort()
方法会在 DOM 请求完成之前中止它。它能够中止 fetch 请求、各种响应主体或者流的消耗。
取消Axios请求
既然 AbortController接口的
abort()
方法可以终止fetch请求、各种响应主体或者流的消耗,那么我们考虑将其和axios结合,来取消axios的请求。
查看axios官网,也给出了相关介绍:
为了便于在项目中使用,我们在对其进行一个简单的封装,示例如下:
javascript
//axios配置
function createRequest() {
const request = axios.create({
baseURL: "https://geo.datav.aliyun.com",
headers: {
"Content-Type": "application/json;charset=utf-8",
}
})
const cachePool = new Map()
const encode = (baseURL, method, url, params) => {
const str = `${baseURL}_${url}_${method}_${JSON.stringify(params || {})}`;
const encoder = new TextEncoder();
//接受一个字符串作为输入,返回一个包含 UTF-8 编码的文本的 Uint8Array
const bytes = encoder.encode(str)
//使用Base64编码算法进行编码:将一个二进制字符串(例如,将字符串中的每一个字节都视为一个二进制数据字节)编码为 Base64 编码的 ASCII 字符串
const encoded = btoa(String.fromCharCode(...bytes))
return encoded
}
/**
* 对Axios请求实例的config进行编码
* */
const configEncode = (config) => {
//获取基本信息
const baseURL = config.baseURL,
method = config.method,
url = config.url,
params = config?.params || config?.data || {};
//返回编码结果
return encode(baseURL, method, url, params);
}
//请求拦截器
request.interceptors.request.use(
(config) => {
// 在发送请求之前做些什么
console.log(config)
const controller = new AbortController()
config.signal = controller.signal
//根据config配置信息进行编码
const encodeKey = configEncode(config)
console.log("encodeKey:", encodeKey)
//判断请求是否存在
if (cachePool.get(encodeKey)) {
controller.abort()
console.log('cachePool--cancel:', cachePool)
} else {
cachePool.set(encodeKey, { abort: controller })
console.log('cachePool--set:', cachePool)
}
return config;
},
(error) => {
// 对请求错误做些什么
console.log(error);
return Promise.reject(error);
}
);
//响应拦截器
// 添加响应拦截器
request.interceptors.response.use(function (response) {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
const encodeKey = configEncode(response.config)
console.log('response---:', response, encodeKey)
//缓存对象
const cacheItem = cachePool.get(encodeKey)
if (cacheItem) {
console.log("res-success:删除缓存对象")
cachePool.delete(encodeKey)
}
return response;
}, function (error) {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
console.log('axios-error:', error)
if (error.code === "ERR_CANCELED") {
//被取消的axios请求
console.warn(`被取消的重复请求~`)
} else {
//其它错误
return Promise.reject(error);
}
});
//返回
return request
}
接下来做个简单的测试,
javascript
const request = createRequest()
const getData = () => {
return request.get("/areas_v3/bound/420800_full.json", {
params: {
a: 1
}
})
}
getData().then(result => {
console.log(result)
})
getData().then(result => {
console.log(result)
})
getData().then(result => {
console.log(result)
})
查看执行结果:连续发送了3次请求,后两个被取消掉,最终只有一个请求正常返回了请求结果。