重复网络请求限制
1.UI控制
- 添加loading的遮罩层 ,限制后续用户交互.
- 在按钮上触发后,添加disable,限制后续用户交互.
2.js 节流方式控制同一个时间的方法触发频次
重复的事件通过节流方式,只触发第一次,后续一定时间内都不响应(通过固定时间间隔限制)
//节流函数
function throttle(fn,delay) {
let last = 0;
return (...args) => {
let now = Date.now();//now的时间单位是 毫微秒 和 settimeout的delay是一致
console.log("now",now,"last",last)
if(now > last + delay ){
last = now;
fn.apply(this,args)//立刻执行原来定义的函数+ 参数
}
}
}
function hello(info) {
console.log("hello jason",info)
}
const testFn = throttle(hello,300)
testFn("11")
testFn("22")
testFn("33")
setTimeout(() => {
testFn("44")
}, 2000);
3. 参数一样,上一次被取消,当前继续执行
request阶段
- 先判断map是否有一样的key。有则取出并取消
- 继续当前网络请求,并把当前加入map
response阶段
- 把当前请求的key在map移除
import axios from 'axios';
import qs from 'qs'; // 记得引入qs
const CancelToken = axios.CancelToken;
const requestMap = new Map<string, Function>(); // 保存 cancel 函数
// 请求拦截器
axios.interceptors.request.use(
config => {
const keyString = qs.stringify(
Object.assign({}, { url: config.url, method: config.method }, config.data)
);
// 如果存在相同的请求,则取消上一次请求
if (requestMap.has(keyString)) {
const cancel = requestMap.get(keyString);
if (cancel) {
cancel('取消上一次请求');
}
requestMap.delete(keyString); // 取消后清理掉
}
// 创建新的 cancelToken,并存储 cancel 函数
config.cancelToken = new CancelToken(cancel => {
requestMap.set(keyString, cancel);
});
// 保存key到config,后续响应拦截器用
(config as any)._keyString = keyString;
// post、put、delete请求,body参数要序列化
if (config.method === 'post' || config.method === 'put' || config.method === 'delete') {
config.data = qs.stringify(config.data);
}
return config;
},
error => {
return Promise.reject(error);
}
);
// 响应拦截器
axios.interceptors.response.use(
res => {
const config = res.config as any;
if (config._keyString) {
requestMap.delete(config._keyString); // 请求成功后,清理 map
}
return res;
},
error => {
const config = error.config as any;
if (config && config._keyString) {
requestMap.delete(config._keyString); // 出错也要清理
}
return Promise.reject(error);
}
);
4.统一汇总某一刻所有请求,参数一致的网络请求的不再发起,取消当前请求
request阶段
- 判断当前网络请求key是否在里面,在则直接取消当前请求,当前请求会被劫持
- 不在的话加入map,然后继续执行
response阶段
- 把当前请求的key在map移除
import axios from 'axios';
const CancelToken = axios.CancelToken;
const requestMap = new Map();
// 请求前置拦截器
Axios.interceptors.request.use(
config => {
// 防重复提交
const keyString = qs.stringify(Object.assign({}, { url: config.url, method: config.method }, config.data));
if (requestMap.get(keyString)) {
// 取消当前请求
config.cancelToken = new CancelToken((cancel) => {
cancel('取消请求');
});
}
requestMap.set(keyString, true);
Object.assign(config, { _keyString: keyString });
if (config.method === 'post' || config.method === 'put' || config.method === 'delete') {
// 序列化
config.data = qs.stringify(config.data);
}
return config;
},
error => {
return Promise.reject(error);
}
);
// 返回响应拦截器
Axios.interceptors.response.use(
(res: any) => {
// 重置requestMap
const config: any = res.config;
requestMap.set(config._keyString, false);
if (res.status === 200) {
return res;
}
},
error => {
return Promise.reject({ error })
}
);
取消第二种方式
if (requestMap.get(keyString)) {
// 方法2 直接手动 reject 一个 Cancel 错误
return Promise.reject(new axios.Cancel(`Duplicate request canceled: ${keyString}`));
}
常见的前端网络库
XMLHttpRequest
早期的底层网络请求库, jQuery 的 ajax 和 axios 都是基于他做封装
fetch
新的底层网络库,支持 promise ,性能更优
终止网络的方式
XMLHttpRequest.abort()
// 创建XMLHttpRequest对象
const xhr = new XMLHttpRequest();
// 请求地址
const url = "https://api.github.com";
// 初始化请求
xhr.open("GET", url, true);
// 发送请求
xhr.send();
// 监听取消请求
xhr.addEventListener("abort", function () {
console.log("请求被abort()取消了");
});
// 定时器模拟取消请求
setTimeout(() => {
// 取消请求
xhr.abort();
// 取消请求之后的状态status
console.log("abort()之后的xhr.status---", xhr.status);
// 取消请求之后的状态readyState
console.log("abort()之后的xhr.readyState---", xhr.readyState);
}, 100);
axios 的 AbortController
// 以vue项目中使用axios为例
// 创建请求控制器
this.controller = new AbortController();
// 第一种方法:绑定事件处理程序
this.controller.signal.addEventListener("abort", () => {
console.log("请求已终止,触发了onabort事件");
// 进行后续处理
});
// 第二种方法:try...catch
try {
// 发送文件上传请求
const res = await this.$axios.post('https://api.github.com', {}, {
timeout: 0, // 设置超时时间为 0/null 表示永不超时
signal: this.controller.signal, // 绑定取消请求的信号量
});
} catch (error) {
console.log("终止请求时catch的error---", error);
// 判断是否为取消上传
if (error.message == "canceled") {
// 进行后续处理
}
}
// 终止请求
this.controller.abort();
axios 的 CancelToken
新写法axios.cancelToken.source()
import axios from "axios";
this.source = axios.CancelToken.source();
console.log("初始声明的请求令牌---", this.source);
// 第二种方法:try...catch
try {
// 发送文件上传请求
const res = await this.$axios.post(
"https://api.github.com",
{},
{
timeout: 0, // 设置超时时间为 0/null 表示永不超时
cancelToken: this.source.token, // 绑定取消请求的令牌
}
);
} catch (error) {
console.log("终止请求时catch的error---", error);
// 判断是否为取消上传
if (error.message == "自定义取消请求的message") {
// 进行后续处理
}
}
// 终止请求
this.source.cancel("自定义取消请求的message");
console.log("取消请求后的请求令牌---", this.source);
旧写法
const CancelToken = axios.CancelToken;
let cancel;
// 1. 发请求时,自己new一个CancelToken
axios.get('/some/api', {
cancelToken: new CancelToken(function executor(c) {
cancel = c; // 将 cancel 函数暴露出去
})
});
// 2. 需要取消时,调用 cancel()
cancel('取消请求');
fetch 的 AbortController
使用方法跟axios类似
const controller = new AbortController();
const { signal } = controller;
fetch("https://api.github.com/", {
signal,
}).then((response) => {
console.log(`Request 2 is complete! responce:`, response);
}).catch((e) => {
console.warn(`Fetch 2 error: ${e.message}`);
});
// abort request
controller.abort();