前端axios取消請求總結(jié)詳解
應(yīng)用場景
取消請求在前端有時候會用到,以下是兩個工作中可能會用到的場景
- tab切換時刷新某個列表數(shù)據(jù),如果他們共用一個變量存儲數(shù)據(jù)列表,當(dāng)請求有延時,可能會導(dǎo)致兩個tab數(shù)據(jù)錯亂;
- 導(dǎo)出文件或下載文件時,中途取消 。
如何取消請求
取消http請求,axios文檔里提供了兩種用法:
第一種:使用 CancelToken
const { CancelToken, isCanCel } = axios; const source = CancelToken.source(); axios.get('/user/12345', { cancelToken: source.token }).catch(thrown => { if (isCancel(thrown)) { // 獲取 取消請求 的相關(guān)信息 console.log('Request canceled', thrown.message); } else { // 處理其他異常 } }); axios.post('/user/12345', { name: 'new name' }, { cancelToken: source.token }) // 取消請求。參數(shù)是可選的,參數(shù)傳遞一個取消請求的相關(guān)信息,在 catch 鉤子函數(shù)里能獲取到 source.cancel('Operation canceled by the user.');
第二種:給構(gòu)造函數(shù) CancelToken 傳遞一個 executor 函數(shù)作為參數(shù)。這種方法的好處是,可以用同一個 cancel token 來取消多個請求
const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { // 參數(shù) c 也是個函數(shù) cancel = c; }) }); // 取消請求,參數(shù)用法同上 cancel();
項目中用法示例
在一個真實的項目中,一般都會對axios進行二次封裝,針對請求、響應(yīng)、狀態(tài)碼、code等做處理。貼一個項目里常用的request.js:
import axios from 'axios' import store from '@/store' import { getToken } from '@/utils/auth' // 創(chuàng)建一個 axios 實例,并改變默認配置 const service = axios.create({ baseURL: process.env.BASE_API, // api 的 base_url timeout: 5000 // request timeout }) // 請求攔截 service.interceptors.request.use( config => { // Do something before request is sent if (store.getters.token) { // 讓每個請求攜帶token-- ['X-Token']為自定義key 請根據(jù)實際情況自行修改 config.headers['X-Token'] = getToken() } return config }, error => { // Do something with request error console.log(error) // for debug Promise.reject(error) } ) // 響應(yīng)攔截 service.interceptors.response.use( response => response, error => { alert(error) return Promise.reject(error) } ) export default service
對于某一個請求添加取消的功能,要在調(diào)用api時,加上cancelToken選項,使用時的示例:
// api.js import request from 'request' export function getUsers(page, options) { return request({ url: 'api/users', params: { page }, ...options }) }
// User.vue import { CancelToken, isCancel } from 'axios' import { getUsers } from 'api' ... cancel: null ... toCancel() { this.cancel('取消請求') } getUsers(1, { cancelToken: new CancelToken(c => (this.cancel = c)) } ) .then(...) .catch(err => { if (isCancel) { console.log(err.message) } else { ... } })
以上,我們就可以順順利利地使用封裝過的axios,取消某一個請求了。其原理無非就是把cancelToken的配置項,在調(diào)用api時加上,然后就可以在業(yè)務(wù)代碼取消特定請求了。
批量取消請求
在 document 里的第二種方法已經(jīng)說過:通過指定同一個cancel token來取消。但是,在上面的項目示例中,不能控制拿到相同的cancel token。我們可以換個思路:用數(shù)組保存每個需要取消的cancel token,然后逐一調(diào)用數(shù)組里的每一項即可:
// User.vue import { CancelToken, isCancel } from 'axios' import { getUsers } from 'api' ... cancel: [] ... toCancel() { while (this.cancel.length > 0) { this.cancel.pop()('取消請求') } } getUser1(1, { cancelToken: new CancelToken(c1 => (this.cancel.push(c1))) } ) getUser2(2, { cancelTokem: new CancleTokem(c2 => (this.cancel.push(c2))) } )
切換路由時,取消請求
上面講了取消一個請求及頁面內(nèi)批量abort的方法,此外,還有一種需求——切換路由時,取消所有。 這里不詳細贅述了,大概思路就是在請求攔截器里,統(tǒng)一加個token,并設(shè)置全局變量source控制一個cancel token,在路由變化時調(diào)用cancel方法。
http.interceptors.request.use(config => { config.cancelToken = store.source.token return config }, err => { return Promise.reject(err) }) router.beforeEach((to, from, next) => { const CancelToken = axios.CancelToken store.source.cancel && store.source.cancel() store.source = CancelToken.source() next() }) // 全局變量 store = { source: { token: null, cancel: null } }
取消請求的實現(xiàn)原理
cancelToken的source方法維護了一個對象,里面包括了token令牌和cancel方法,token來自與構(gòu)造函數(shù)CancelToken,調(diào)用cancel方法后,token的promise狀態(tài)為resolved,進而又調(diào)用了xhr的abort方法,取消請求成功。 來分析下取消請求是怎么實現(xiàn)的,先從一個簡單的取消請求的例子開始:
var CancelToken = axios.CancelToken; var source = CancelToken.source(); axios.get('/get?name=xmz', { cancelToken : source.token }).then((response)=>{ console.log('response', response) }).catch((error)=>{ if(axios.isCancel(error)){ console.log('取消請求傳遞的消息', error.message) }else{ console.log('error', error) } }) // 取消請求 source.cancel('取消請求傳遞這條消息');
這就是一個簡單的取消請求的例子,那么就從最開始的axios.CancelToken
來看,先去axios/lib/axios.js
文件中。
axios.CancelToken = require('./cancel/CancelToken');
不費吹灰之力,就找到了CancelToken,在例子中我們調(diào)用了source方法,那么就去axios/lib/cancel/CancelToken.js
文件中看看這個source方法到底是干什么的?
CancelToken.source = function(){ var cancel; var token = new CancelToken(function executor(c) { cancel = c }) return { token : token, cancel : cancel } }
source方法很簡單,就是返回一個具有token和cancel屬性的對象,但是token和cancel都是通過CancelToken這個構(gòu)造函數(shù)來的,那么還在這個文件中向上看,找到CancelToken函數(shù)。
function CancelToken (executor){ // ... // 判斷executor是一個函數(shù),不然就報錯 var resolvePromise; this.promise = new Promise(function(resolve){ resolvePromise = resolve; }) var token = this; // 以上token現(xiàn)在有一個promise屬性,是一個未成功的promise對象; executor(function cancel(message){ if(token.reason){ return; } token.reason = new Cancel(message); resolvePromise(token.reason); }) // 這個cancel函數(shù)就是 上面函數(shù)中的cancel,也就是source.cancel; }
現(xiàn)在知道了source.cancel是一個函數(shù),souce.token是一個實例化對象,暫時就知道這些,繼續(xù)看文章最開始的例子,接下來是去發(fā)送請求了,最下面還有一行代碼是執(zhí)行souce.cancel();
souce.cancel就是用來觸發(fā)取消請求的函數(shù)。 現(xiàn)在再回頭來看,上面的cancel函數(shù),cancel執(zhí)行,給token加了一個reason屬性,那么看下這個reason屬性是什么吧,看下這個Cancel構(gòu)造函數(shù),在axios/lib/cancel/Cancel.js
文件中
function Cancel(message){ this.message = message }
Cancel特別簡單就是給實例化對象添加一個message屬性,所以現(xiàn)在token.reason是一個具有message屬性的對象了。 繼續(xù)回到cancel函數(shù)中,resolvePromise函數(shù)執(zhí)行了,那么token.promise對象,這個原本未變成,成功狀態(tài)的promise,變成了成功狀態(tài)了,并且將token.reason對象傳遞過去了。 簡單總結(jié)一下,執(zhí)行取消函數(shù),就是讓token的promise的狀態(tài)變成了成功; 好了,突然發(fā)現(xiàn)分析中斷了,變成成功狀態(tài)又怎樣了,怎么取消的呢?雖然現(xiàn)在的同步代碼都執(zhí)行完了,但是請求還沒發(fā)送出去呢,我們還要去看發(fā)送請求的函數(shù)
在分析發(fā)送請求之前,再看下最開始的例子,和最普通的發(fā)送一個get請求還是有一點區(qū)別的,配置對象中多了,一個cancelToken的屬性,值是token,到底起了什么作用呢,去axios/lib/adapters/xhr.js
中一探究竟(這里只截取其中關(guān)于cancelToken的部分)。
// 在發(fā)送請求之前,驗證了cancelToken,看來此處就是用來取消請求的; if(config.cancelToken){ // 具體是如何取消的,是在這個判斷內(nèi)定義的; config.cancelToken.promise.then(function(cancel){ request.abort(); reject(cancel); request = null; }) } // 發(fā)送請求 request.send(requestData);
仔細看這只是一個promise的then函數(shù),只有在promise的狀態(tài)變成成功后才會執(zhí)行,而剛才我們分析了,cancel就是讓這個promise的狀態(tài)變成成功,所以如果執(zhí)行了,取消請求的函數(shù),這個then就會執(zhí)行,取消發(fā)送請求,并且把發(fā)送請求的promise變成reject,被axiox.get().catch()捕獲; 流程已經(jīng)清楚了,最后再總結(jié)一下: 執(zhí)行cancel是讓token的promise變成成功,在真正發(fā)送請求之前,驗證token.promise的狀態(tài)是否已經(jīng)變了,如果變了,就取消請求,就是這樣一個簡單的思想來進行取消請求的。
以上就是axios取消請求總結(jié)的詳細內(nèi)容,更多關(guān)于axios取消請求的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue2 設(shè)置router-view默認路徑的實例
今天小編就為大家分享一篇vue2 設(shè)置router-view默認路徑的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-09-09ElementUI實現(xiàn)el-table行列合并的操作步驟
在前端開發(fā)中,數(shù)據(jù)展示一直是一個重要的部分,而表格則是數(shù)據(jù)展示最常見的形式之一,ElementUI 是餓了么前端團隊推出的一款基于 Vue 的 UI 組件庫,其中的 el-table 組件是一個功能強大且靈活的表格組件,今天我們要詳細探討的是 el-table 的行列合并操作2024-08-08淺談Vue static 靜態(tài)資源路徑 和 style問題
這篇文章主要介紹了淺談Vue static 靜態(tài)資源路徑 和 style問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11