项目开发中有时遇到网络请求时间过长导致 pending, 需要主动去中断网络请求。例如在搜索框远程搜索数据,由于数据量比较大,搜索比较慢,设计当鼠标失去焦点时主动中段网络请求,以便进行其它操作。 在 JavaScript 中,前端可以通过 AbortController 来主动中断网络请求。AbortController 是一个全局对象,它允许你创建一个信号(signal),可以传递给许多API,包括 fetch,来中止请求。 代码如下:
const controller = new AbortController();
const signal = controller.signal;
// 请求网络数据函数
const fetchWithToken = (url:any) => {
const headers = new Headers({
'Authorization': `Bearer ${useUserStore().token}`,
});
const fetchOptions = {
method: 'GET', // 使用适当的 HTTP 方法
headers: headers,
signal: controller.signal, // 将 signal 传递给 fetch
};
fetch(url, fetchOptions)
.then((response: any) => {
if (!response.ok) {
throw new Error('网络请求失败');
}
return response.json(); // 将响应数据转换为 JSON
})
.then(data => {
// 返回数据处理
console.log(data)
})
.catch(error => {
if (error.name === 'AbortError') {
console.log('Fetch aborted, retrying...');
} else {
console.error('Fetch error:', error);
}
})
};
// 如果你想中断请求,可以调用controller.abort()
// fetchPromise将会 reject,并抛出一个AbortError
controller.abort();
在具体项目中的代码使用如下:
const controller = new AbortController();
const signal = controller.signal;
// 请求网络数据函数
const fetchWithToken = () => {
const headers = new Headers({
'Authorization': `Bearer ${useUserStore().token}`,
});
const fetchOptions = {
method: 'GET', // 使用适当的HTTP方法
headers: headers,
signal: controller.signal, // 将signal传递给fetch
};
fetch(import.meta.env.VITE_AXIOS_BASEURL + '/' + Url.brandAndPartNo + '?brand=&partNo=' + searchKeyword, fetchOptions)
.then((response: any) => {
if (!response.ok) {
throw new Error('网络请求失败');
}
return response.json(); // 将响应数据转换为JSON
})
.then(data => {
// 数据处理
})
.catch(error => {
if (error.name === 'AbortError') {
console.log('Fetch aborted, retrying...');
// fetchWithToken();
} else {
console.error('Fetch error:', error);
}
})
};
// 输入关键字搜索,调用 fetchWithToken 方法
const searchPartNo = async (keyword: any) => {
if (!keyword || keyword.length < 3) {
return
}
partNos.value = []
spinning.value = true
searchKeyword = keyword.toUpperCase()
fetchWithToken()
}
// 失去焦点时触发
const onBlur = () => {
// 中断网络请求
controller.abort()
};
这样确实在失去焦点时主动中断了网络请求,但是会产生一个 bug, 就是我再次发起网络请求时失效。网络不会发起请求,查找原因发现是因为在 controller.abort() 中断网络请求后,signal.aborted 参数被设置为了 true,表示已经中断,所以再次调用 fetchWithToken 函数是无效的,由于这个参数是只读,不能对其更改,所以解决办法就是重新创建 AbortController。新的 signal.aborted 就恢复为了 false。
代码修改如下:
// 把 controller 设置为变量,初始化为 null
let controller:any = null
// 输入关键字搜索
const searchPartNo = async (keyword: any) => {
if (!keyword || keyword.length < 3) {
return
}
// 如果 controller 不为 null,就将 controller 初始化
if(controller){
controller.abort()
controller = null
}
// 否则就创建新的 AbortController 赋值
controller = new AbortController()
partNos.value = []
spinning.value = true
searchKeyword = keyword.toUpperCase()
fetchWithToken()
}
这样修改就解决了网络请求中断后,再次请求无效的问题。