一、我們?yōu)槭裁葱枰∠?/h3>
我們常見的業(yè)務場景有,用戶頻繁點擊數(shù)據(jù)提交,用戶頻繁的切換數(shù)據(jù)量大的表格,在網(wǎng)絡較差的環(huán)境下,會出現(xiàn)一些異常的情況,當然,各類UI庫提供的loading狀態(tài)可以幫我們解決以上煩惱,以及可以通過防抖解決部分問題,但是我們今天從另一個方面,使用axios的cancelToken取消上次重復的請求。
二、我們怎樣取消?
先看一下aixos官方文檔的cancelToken的代碼
方法1:可以使用 CancelToken.source 工廠方法創(chuàng)建 cancel token,像這樣:
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/user/12345', {
cancelToken: source.token
}).catch(function(thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// 處理錯誤
}
});
axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})
// 取消請求(message 參數(shù)是可選的)
source.cancel('Operation canceled by the user.');
方法2:還可以通過傳遞一個 executor 函數(shù)到 CancelToken 的構(gòu)造函數(shù)來創(chuàng)建 cancel token:
const CancelToken = axios.CancelToken;
let cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
// executor 函數(shù)接收一個 cancel 函數(shù)作為參數(shù)
cancel = c;
})
});
// cancel the request
cancel();
如何在我們項目中使用呢?
我們項目中,更常用的做法是在攔截器interceptors中使用。
當然,我們需要先定義一個map對象用于存儲相關值(map為es6新增對象,如果你對es6不了解,請查閱阮一峰的ECMAScript 6 入門
// 用于存儲pending的請求(處理多條相同請求)
const pendingRequest = new Map()
其次,需要封裝一些輔助類的函數(shù)
- generateRequestKey 將重復的請求生成一個唯一的key
import qs from 'qs'
// 生成request的唯一key
const generateRequestKey = (config = {}) => {
// 通過url,method,params,data生成唯一key,用于判斷是否重復請求
// params為get請求參數(shù),data為post請求參數(shù)
const { url, method, params, data } = config
return [url, method, qs.stringify(params), qs.stringify(data)].join('&')
}
- addPendingRequest將重復的請求,保存到到
pendingRequest中
// addPendingRequest 將重復請求添加到pendingRequest中
const addPendingRequest = (config) => {
const key = generateRequestKey(config)
if (!pendingRequest.has(key)) { //判斷pendingRequest中是否存在key
config.cancelToken = new axios.CancelToken(cancel => {
pendingRequest.set(key, cancel) //將key和cancel以鍵值對的方式保存
})
}
}
- 將重復的請求取消,并在
pendingRequest中刪除
// removePendingRequest 取消重復請求
const removePendingRequest = (config) => {
const key = generateRequestKey(config)
if (pendingRequest.has(key)) { //判斷pendingRequest中是否存在key
const cancelToken = pendingRequest.get(key)
cancelToken(key) // 取消之前發(fā)送的請求
pendingRequest.delete(key)// 請求對象中刪除requestKey
}
}
完整代碼
import axios from 'axios'
import qs from 'qs'
// 用于存儲pending的請求(處理多條相同請求)
const pendingRequest = new Map()
// 生成request的唯一key
const generateRequestKey = (config = {}) => {
// 通過url,method,params,data生成唯一key,用于判斷是否重復請求
// params為get請求參數(shù),data為post請求參數(shù)
const { url, method, params, data } = config
return [url, method, qs.stringify(params), qs.stringify(data)].join('&')
}
// 將重復請求添加到pendingRequest中
const addPendingRequest = (config) => {
const key = generateRequestKey(config)
if (!pendingRequest.has(key)) {
config.cancelToken = new axios.CancelToken(cancel => {
pendingRequest.set(key, cancel)
})
}
}
// 取消重復請求
const removePendingRequest = (config) => {
const key = generateRequestKey(config)
if (pendingRequest.has(key)) {
const cancelToken = pendingRequest.get(key)
cancelToken(key) // 取消之前發(fā)送的請求
pendingRequest.delete(key)// 請求對象中刪除requestKey
}
}
// 請求攔截器
service.interceptors.request.use(
config => {
// 處理重復請求
removePendingRequest(config)
addPendingRequest(config)
return config
},
error => {
// 處理請求錯誤
return Promise.reject(error)
}
)
// 響應攔截器
service.interceptors.response.use(
response => {
// 移除重復請求
removePendingRequest(response.config)
return res
},
error => {
// 異常情況console,方便排查問題
console.log('error', error)
// 移除重復請求
removePendingRequest(error.config || {})
return Promise.reject(error)
}
)
三、結(jié)尾
代碼中如有需要更正和修改的地方,歡迎評論留言,我也會積極回復。