<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
本文所用 axios 版本號為:1.3.2
。
axios 中有兩種攔截器:
axios.interceptors.request.use(onFulfilled, onRejected, options)
:設定請求攔截器。
axios.interceptors.response.use(onFulfilled, onRejected, options)
:設定響應攔截器。axios.interceptors.request.use
。axios.interceptors.request.use( function (config) { return config; }, function (error) { return Promise.reject(error); } ); axios.interceptors.response.use( function (response) { return response; }, function (error) { return Promise.reject(error); } );
可以新增多個攔截器:
axios.interceptors.request.use( function (config) { throw new Error("999"); return config; }, function (error) { console.log(1, error); return Promise.reject(error); } ); axios.interceptors.request.use( function (config) { throw new Error("888"); return config; }, function (error) { console.log(2, error); return Promise.reject(error); } ); axios.interceptors.response.use( function (response) { return response; }, function (error) { console.log(3, error); return Promise.reject(error); } ); axios.interceptors.response.use( function (response) { return response; }, function (error) { console.log(4, error); return Promise.reject(error); } ); axios.get("https://www.baidwwu.com").catch((error) => { console.log(4, error); }); // 2 Error: 888 // 1 Error: 888 // 3 Error: 888 // 4 Error: 888 // 5 Error: 888
先執行請求攔截器,後執行響應攔截器。
先來看 use() 方法相關程式碼:
this.interceptors = { request: new InterceptorManager$1(), response: new InterceptorManager$1(), }; var InterceptorManager = (function () { function InterceptorManager() { this.handlers = []; } // ... // _createClass 方法可以給物件的原型上新增屬性 _createClass(InterceptorManager, [ { key: "use", value: function use(fulfilled, rejected, options) { this.handlers.push({ fulfilled: fulfilled, rejected: rejected, synchronous: options ? options.synchronous : false, runWhen: options ? options.runWhen : null, }); return this.handlers.length - 1; }, }, { key: "forEach", value: function forEach(fn) { utils.forEach(this.handlers, function forEachHandler(h) { if (h !== null) { fn(h); } }); }, }, // ... ]); return InterceptorManager; })(); var InterceptorManager$1 = InterceptorManager;
可以看到 interceptors.request
和 interceptors.response
各自指向 InterceptorManager
的範例。
InterceptorManager
原型上設定了 use()
方法,執行後給待執行佇列 this.handlers
中新增一條資料。
這裡利用了 this
的特性來區分作用域:誰呼叫 use()
方法就給誰的 handlers
中新增資料。在呼叫 use()
方法之後,已經將回撥函數按順序新增到了 handlers
陣列中。
forEach()
方法用來遍歷當前作用域 handlers
中不為 null
的元素,在執行攔截器時有用到,詳情見下文。
攔截器是在呼叫了 request()
方法前後執行的,先看相關原始碼:
var Axios = (function () { _createClass(Axios, [ { key: "request", value: function request(configOrUrl, config) { // ... // 初始化請求攔截器 var requestInterceptorChain = []; var synchronousRequestInterceptors = true; this.interceptors.request.forEach(function unshiftRequestInterceptors( interceptor ) { if ( typeof interceptor.runWhen === "function" && interceptor.runWhen(config) === false ) { return; } synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous; requestInterceptorChain.unshift( interceptor.fulfilled, interceptor.rejected ); }); // 初始化響應攔截器 var responseInterceptorChain = []; this.interceptors.response.forEach(function pushResponseInterceptors( interceptor ) { responseInterceptorChain.push( interceptor.fulfilled, interceptor.rejected ); }); var promise; var i = 0; var len; // 請求攔截器同步執行模式 if (!synchronousRequestInterceptors) { var chain = [dispatchRequest.bind(this), undefined]; chain.unshift.apply(chain, requestInterceptorChain); chain.push.apply(chain, responseInterceptorChain); len = chain.length; promise = Promise.resolve(config); console.log(11, chain); while (i < len) { promise = promise.then(chain[i++]).catch(chain[i++]); } return promise; } // 請求攔截器非同步執行模式 len = requestInterceptorChain.length; var newConfig = config; i = 0; while (i < len) { var onFulfilled = requestInterceptorChain[i++]; var onRejected = requestInterceptorChain[i++]; try { newConfig = onFulfilled(newConfig); } catch (error) { onRejected.call(this, error); break; } } try { promise = dispatchRequest.call(this, newConfig); } catch (error) { return Promise.reject(error); } i = 0; len = responseInterceptorChain.length; while (i < len) { promise = promise.then( responseInterceptorChain[i++], responseInterceptorChain[i++] ); } return promise; }, }, ]); })();
上面是相關的全部程式碼,下面進行分解。
var requestInterceptorChain = []; // 請求攔截器執行鏈 // 是否同步執行請求攔截器,每個攔截器都可以單獨設定,但是隻有所有攔截器都設定為true才為true var synchronousRequestInterceptors = true; this.interceptors.request.forEach(function unshiftRequestInterceptors( interceptor ) { // 傳入 runWhen() 方法時,如果 runWhen() 方法返回值為 false 則忽略這個請求攔截器 if ( typeof interceptor.runWhen === "function" && interceptor.runWhen(config) === false ) { return; } // 是否同步執行 synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous; // 用 unshift() 新增資料,後設定的攔截器在前 requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected); }); var responseInterceptorChain = []; // 響應攔截器執行鏈 this.interceptors.response.forEach(function pushResponseInterceptors( interceptor ) { // 響應攔截器按順序加在後面 responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected); });
interceptors.request.forEach()
的定義在上文提到過,封裝為遍歷自身的 handlers
陣列,與陣列的 forEach
類似,只是過濾了值為 null
的元素。
runWhen()
方法接收 config,返回 boolean,控制是否將當前攔截器的回撥函數新增到執行鏈中。
請求攔截器用 unshift()
方法新增,所以後設定的先執行,響應攔截器用 push()
方法新增,所以按照設定順序執行。
只要有一個攔截器的 synchronous
設定為 false
,則 synchronousRequestInterceptors
的值為 false
。
synchronousRequestInterceptors
為 false
時為同步執行,相關邏輯如下:
var promise; var i = 0; var len; if (!synchronousRequestInterceptors) { var chain = [dispatchRequest.bind(this), undefined]; // 執行鏈 chain.unshift.apply(chain, requestInterceptorChain); // 將請求攔截器新增到請求之前 chain.push.apply(chain, responseInterceptorChain); // 將響應攔截器新增到響應之後 len = chain.length; promise = Promise.resolve(config); while (i < len) { promise = promise.then(chain[i++], chain[i++]); // 用 Promise 包裝執行鏈 } return promise; }
dispatchRequest()
是傳送請求的方法。
同步執行模式下,會將執行鏈中的所有方法用 Promise 進行封裝,前一個方法執行完畢後將其返回值作為引數傳遞給下一個方法。
這裡的 chain
其實就是所有攔截器方法與請求方法合併而成的執行鏈,等價於: [...requestInterceptorChain, dispatchRequest.bind(this), ...responseInterceptorChain]
。
從一個例子來看 chain
的成員:
axios.interceptors.request.use(onFulfilled1, onRejected1); axios.interceptors.request.use(onFulfilled2, onRejected2); axios.interceptors.response.use(onFulfilled3, onRejected3); axios.interceptors.response.use(onFulfilled4, onRejected4); axios.get(); // chain: [onFulfilled2, onRejected2, onFulfilled1, onRejected1, dispatchRequest, onFulfilled3, onRejected3, onFulfilled4, onRejected4]
在構建 Promise 鏈的時候,一次遍歷中取了兩個方法傳遞給 then():
promise = Promise.resolve(config); while (i < len) { promise = promise.then(chain[i++], chain[i++]); // 用 Promise 包裝執行鏈 } return promise;
var promise; var i = 0; var len = requestInterceptorChain.length; // 請求執行鏈的長度 var newConfig = config; i = 0; // 執行請求攔截器回撥 while (i < len) { var onFulfilled = requestInterceptorChain[i++]; // use() 的第一個引數 var onRejected = requestInterceptorChain[i++]; // use() 的第二個引數 // 執行成功後繼續執行,執行失敗後停止執行。 try { newConfig = onFulfilled(newConfig); } catch (error) { onRejected.call(this, error); break; } } // 執行傳送請求方法 try { promise = dispatchRequest.call(this, newConfig); } catch (error) { return Promise.reject(error); // 執行失敗後退出 } // 執行響應攔截器回撥 i = 0; len = responseInterceptorChain.length; while (i < len) { promise = promise.then( responseInterceptorChain[i++], // use() 的第一個引數 responseInterceptorChain[i++] // use() 的第二個引數 ); } return promise;
dispatchRequest()
是傳送請求的方法。
用 while
迴圈遍歷所有的請求攔截器並呼叫,將執行語句包裹在 try-catch
語句中,只要有一個請求攔截器異常就停止迴圈。
可以看到在非同步模式下,請求攔截器為非同步執行,但是不影響傳送請求,而響應攔截器還是在請求響應後同步執行。
呼叫 .request.use()
和 .response.use()
方法時,將傳入的攔截器回撥方法分別存入 請求攔截器回撥陣列
和 響應攔截器回撥陣列
。
在呼叫 .get()
等方法時,將 請求攔截器回撥陣列
和 響應攔截器回撥陣列
與傳送請求的方法拼接成一個完整的執行鏈,按照同步或非同步的模式呼叫執行鏈中的方法。
響應攔截器總是在返回響應結果後按順序執行。
請求攔截器根據 synchronous
設定不同,行為有所不同:
synchronous
時預設為非同步執行請求攔截器模式,即遍歷執行所有的請求攔截器一參回撥,執行報錯後停止遍歷,並執行該攔截器的二參回撥。synchronous
為 false
時為同步執行請求攔截器模式,將執行鏈包裝成一個 Promise 鏈順序執行。先執行請求攔截器,後執行響應攔截器。
計算機中的同步,指的是現實中的一步一步(同一時間只能幹一件事),非同步指的是同時進行(同一時間能幹多件事)。
參考 npm axios
以上就是axios攔截器工作方式及原理原始碼解析的詳細內容,更多關於axios攔截器工作原理的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45