小蚊子前端
高級前端工程師ios
咱們在先後端交互的過程當中,一般是經過請求接口來實現的,而一個頁面中的交互又很是複雜,例如須要屢次頻繁請求同一個接口,或者在接口還沒返回時就要切換路由等。這些都須要對接口請求的時機或者請求接口以後進行處理,避免一些無用的請求或者接口返回順序的差別。git
咱們在以前的如何實現 axios 的自定義適配器 adapter文章裏,略過了 axios 是如何主動取消當前請求的。今天咱們就將一下在 axios 中如何取消以前發起的請求,源碼中又是怎樣實現的。github
咱們先來看下 axios 中取消請求的用法:axios
const CancelToken = axios.CancelToken; // 返回兩個字段,{ token, cancel } // token用於表示某個請求,是一個Promise類型 // cancel是一個方法,當被調用時,則取消token注入的那個請求 const source = CancelToken.source(); axios .get('/user/12345', { cancelToken: source.token, // 將token注入到請求中 }) .catch(function (thrown) { // 判斷是不是因主動取消致使的 if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // handle error console.error(thrown); } }); axios.post( '/user/12345', { name: 'new name', }, { cancelToken: source.token, } ); // 主動取消請求 // cancel方法會把注入的同一個token的請求方法一併取消掉 // 上面的get和post請求都會被取消掉 // cancel the request (the message parameter is optional) source.cancel('Operation canceled by the user.');
從 demo 上來看,用法很簡單,token 和 cancel 的關係對應上便可。後端
官方例子中還有一種取消請求的方式,這個咱們放在後面講,更容易理解一些。promise
取消請求的方法在 https://github.com/axios/axios/tree/master/lib/cancel 的目錄中,3 個文件:markdown
source 做爲取消請求的入口,咱們就先來看下 source 方法。前端工程師
// 建立token和cancel方法 CancelToken.source = function source() { var cancel; // token爲 CancelToken 的實例,包含 promise 和 reason 兩個屬性 // 同時把 executor 中的參數給到 cancel // 即CancelToken有一個回調函數,而這個回調函數的參數也是一個函數 // CancelToken怎麼執行,咱們接着看! var token = new CancelToken(function executor(c) { cancel = c; }); return { token: token, cancel: cancel, }; };
CancelToken 用來取消請求,但我理解起來,思路很是的繞,咱們一點點來剖析:ide
function CancelToken(executor) { if (typeof executor !== 'function') { throw new TypeError('executor must be a function.'); } // 建立一個Promise的實例, // 當resolvePromise執行時,this.promise變爲fulfilled狀態 var resolvePromise; this.promise = new Promise(function promiseExecutor(resolve) { resolvePromise = resolve; }); // new一個實例時,會當即執行CancelToken的回調函數executor方法 // executor的參數也是一個函數,即上面的cancel就是當前的cancel函數體 // 當executor的回調函數cancel執行時,會給當前CancelToken建立一個reason屬性,這個屬性是Cancel的實例 // 並執行resolvePromise方法,將reason實例穿進去;執行後this.promise變爲fulfilled狀態 var token = this; executor(function cancel(message) { if (token.reason) { // Cancellation has already been requested return; } token.reason = new Cancel(message); resolvePromise(token.reason); }); }
也就是說會先建立一個 CancelToken 的實例 token,同時,將 CancelToken 中回調函數的參數給到了 cancel。當 cancel 執行時,則 token 中的 promise 屬性則會從 pending 狀態變爲 fulfilled 狀態,那麼 promise 上掛載的then()方法也就能夠繼續執行了。
在調用 cancel 方法後,請求中是怎麼操做的呢?咱們看下adapter/xhr.js中的代碼:
if (config.cancelToken) { // config.cancelToken就是上面建立的token // 當token.promise變爲fulfilled狀態後,就能夠執行後續的鏈式操做 // Handle cancellation config.cancelToken.promise.then(function onCanceled(cancel) { if (!request) { return; } // 取消當前的請求 request.abort(); // 將Cancel的實例cancel給到reject reject(cancel); // Clean up request request = null; }); }
當咱們使用 axios 的 catch 捕獲內部拋出的異常時,就能夠經過isCancel判斷是不是因主動取消請求致使的異常:
axios .get('/user/12345', { cancelToken: source.token, // 將token注入到請求中 }) .catch(function (thrown) { // 判斷是不是因主動取消致使的 if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // handle error console.error(thrown); } });
如今再來看下 cancel 執行的整個流程,就會清晰流暢不少。
咱們在第 1 節還留着一個問題,axios 取消請求還有另外一種方式,即直接使用 CancelToken 類。
const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { // CancelToken建立的 cancelToken: new CancelToken(function executor(c) { // An executor function receives a cancel function as a parameter cancel = c; }), }); // cancel the request cancel();
其實咱們發現,source()方法,只是給咱們額外又封裝了一下,簡單的返回了 token 和 cancel,但本質仍是 CancelToken 中的東西。
在取消請求的過程當中,token 要和 cancel 方法保持對應關係,即都在一個對象裏;若其餘的請求也要取消時,能夠額外再生成一組 token 和 cancel。同時,這裏還用到了 Promise 的一個機制,只有在當前 Promise 變動爲 fulfilled 狀態後,才能執行後面的 then 等操做。