Axios 是一個(gè)基于 Promise 的 HTTP 客戶端袖肥,同時(shí)支持瀏覽器和 Node.js 環(huán)境。它是一個(gè)優(yōu)秀的 HTTP 客戶端油狂,被廣泛地應(yīng)用在大量的 Web 項(xiàng)目中寸癌。
# 如何取消請求
對于瀏覽器環(huán)境來說蒸苇,Axios 底層是利用 XMLHttpRequest 對象來發(fā)起 HTTP 請求。如果要取消請求的話味咳,我們可以通過調(diào)用 XMLHttpRequest 對象上的 abort 方法來取消請求:
let xhr = new XMLHttpRequest();
xhr.open("GET", "https://developer.mozilla.org/", true);
xhr.send();
setTimeout(() => xhr.abort(), 300);
而對于 Axios 來說檬嘀,我們可以通過 Axios 內(nèi)部提供的 CancelToken 來取消請求
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.post('/user/12345', {
name: 'semlinker'
}, {
cancelToken: source.token
})
source.cancel('Operation canceled by the user.'); // 取消請求鸳兽,參數(shù)是可選的
此外,也可以通過調(diào)用 CancelToken 的構(gòu)造函數(shù)來創(chuàng)建 CancelToken全陨,具體如下所示:
const CancelToken = axios.CancelToken;
let cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
cancel = c;
})
});
cancel(); // 取消請求
# 如何判斷重復(fù)請求
當(dāng)請求方式辱姨、請求 URL 地址和請求參數(shù)都一樣時(shí)戚嗅,我們就可以認(rèn)為請求是一樣的。因此在每次發(fā)起請求時(shí)镜悉,我們就可以根據(jù)當(dāng)前請求的請求方式侣肄、請求 URL 地址和請求參數(shù)來生成一個(gè)唯一的 key醇份,同時(shí)為每個(gè)請求創(chuàng)建一個(gè)專屬的 CancelToken吼具,然后把 key 和 cancel 函數(shù)以鍵值對的形式保存到 Map 對象中拗盒,使用 Map 的好處是可以快速的判斷是否有重復(fù)的請求:
import qs from 'qs'
const pendingRequest = new Map();
// GET -> params锥债;POST -> data
const requestKey = [method, url, qs.stringify(params), qs.stringify(data)].join('&');
const cancelToken = new CancelToken(function executor(cancel) {
if(!pendingRequest.has(requestKey)){
pendingRequest.set(requestKey, cancel);
}
})
當(dāng)出現(xiàn)重復(fù)請求的時(shí)候哮肚,我們就可以使用 cancel 函數(shù)來取消前面已經(jīng)發(fā)出的請求,在取消請求之后恼策,我們還需要把取消的請求從 pendingRequest 中移除〕奔簦現(xiàn)在我們已經(jīng)知道如何取消請求和如何判斷重復(fù)請求,下面我們來介紹如何取消重復(fù)請求狮斗。
# 如何取消重復(fù)請求
因?yàn)槲覀冃枰獙λ械恼埱蠖歼M(jìn)行處理改含,所以我們可以考慮使用 Axios 的攔截器機(jī)制來實(shí)現(xiàn)取消重復(fù)請求的功能迄汛。Axios 為開發(fā)者提供了請求攔截器和響應(yīng)攔截器,它們的作用如下:
- 請求攔截器:該類攔截器的作用是在請求發(fā)送前統(tǒng)一執(zhí)行某些操作鹃觉,比如在請求頭中添加 token 字段盗扇。
- 響應(yīng)攔截器:該類攔截器的作用是在接收到服務(wù)器響應(yīng)后統(tǒng)一執(zhí)行某些操作沉填,比如發(fā)現(xiàn)響應(yīng)狀態(tài)碼為 401 時(shí),自動(dòng)跳轉(zhuǎn)到登錄頁斑鼻。
定義輔助函數(shù)
generateReqKey:用于根據(jù)當(dāng)前請求的信息猎荠,生成請求 Key;
function generateReqKey(config) {
const { method, url, params, data } = config;
return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&");
}
addPendingRequest:用于把當(dāng)前請求信息添加到pendingRequest對象中荒叶;
const pendingRequest = new Map();
function addPendingRequest(config) {
const requestKey = generateReqKey(config);
config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => {
if (!pendingRequest.has(requestKey)) {
pendingRequest.set(requestKey, cancel);
}
});
}
removePendingRequest:檢查是否存在重復(fù)請求些楣,若存在則取消已發(fā)的請求。
function removePendingRequest(config) {
const requestKey = generateReqKey(config);
if (pendingRequest.has(requestKey)) {
const cancelToken = pendingRequest.get(requestKey);
cancelToken(requestKey);
pendingRequest.delete(requestKey);
}
}
設(shè)置請求攔截器
axios.interceptors.request.use(
function (config) {
removePendingRequest(config); // 檢查是否存在重復(fù)請求艰猬,若存在則取消已發(fā)的請求
addPendingRequest(config); // 把當(dāng)前請求信息添加到pendingRequest對象中
return config;
},
(error) => {
return Promise.reject(error);
}
);
設(shè)置響應(yīng)攔截器
axios.interceptors.response.use(
(response) => {
removePendingRequest(response.config); // 從pendingRequest對象中移除請求
return response;
},
(error) => {
removePendingRequest(error.config || {}); // 從pendingRequest對象中移除請求
if (axios.isCancel(error)) {
console.log("已取消的重復(fù)請求:" + error.message);
} else {
// 添加異常處理
}
return Promise.reject(error);
}
);
參考 全棧修仙之路 作者阿寶哥 《 Axios 如何取消重復(fù)請求冠桃?》