一、情景需求
調用后台接口需要附帶token信息,那么在每個請求的頭部添加token的做法就不太優雅了;一個網站請求100次,那就得寫添加100次token,假設某天接口有所變動,改起來就十分麻煩了。
公司項目開發使用的框架是angularjs,正好angularjs有一個請求攔截的功能;意思就是,每次請求的發起,請求攔截都能感知,所以添加token的事我們就可以在請求攔截統一處理。
為了保證請求攔截添加token永遠是有效的,我在這里除了添加token,也增加了現有token的過期判斷,如果token不存在,或者存在已過期,都得重新拿一次token。
結果騷操作就來,一個頁面有10個請求,第一個請求觸發token判斷並發現已過期,就去請求新的token,巧的是請求token的接口也出了問題,並未能拿回最新token;於是第二個請求也觸發了請求攔截,發現token用不了,也去請求新token,同樣失敗了。
第三次請求攔截失敗.....第四次,第五次......第十次,如果這個頁面請求更多呢,永無止境了。於是,后台接口成功被我玩炸了。
那么問題來了,請求攔截次數是根據請求觸發的,如果token用不了,我去拿新token的操作不管成功失敗,其實都只用觸發一次,成功了一次搞定,失敗了請求100次也都是多余。那么引出我們的問題,如果讓js函數只執行一次:
二、實現
1.函數重寫
我在js模式第五篇博客有提過函數重寫,在這里就是十分適用了:
function fn() { //do something... console.log(1); //函數重寫 fn = function () {}; }; fn();//1 fn();//無作為
2.通過變量控制
原理很簡單,聲明一個默認為true的變量,在執行一次后修改為false,通過條件判斷是否需要執行:
let value = true; function fn(){ if(value){ //do something console.log(1); //執行一次后將變量改為false value = false; }; }; fn();//1 fn();//無作為
3.利用閉包
我們可以利用閉包封裝一個通用函數,將你需要只執行一次的函數作為參數傳入閉包,也可以達到類似的效果:
//封裝執行一次通用函數 function once(fn) { if (Object.prototype.toString.call(fn) !== "[object Function]") { throw new Error('請傳遞一個函數'); }; return function (...rest) { if (fn) { fn.apply(this, rest); fn = null; }; }; }; //我們希望只執行一次的函數 function fn(a, b) { console.log(a + b); }; //調用閉包函數 let onlyOnce = once(fn); onlyOnce(1, 2); //3 onlyOnce(); //無作為
我在網上看到了另外一種寫法,原理類似,適用於一次執行的函數有返回值的情況,在第一次調用后,不管再調用幾次都將跳過執行過程,直接返回第一次執行的值,也非常實用:
function once(fn) { let can, result; if (Object.prototype.toString.call(fn) !== "[object Function]") { throw new Error('請傳遞一個函數'); }; return function (...rest) { if (can) { return result; }; can = true; result = fn.apply(this, rest); //釋放fn保存的函數,便於被垃圾回收 fn = null; return result; }; }; function fn() { return 1; }; let o = once(fn); o();//1 o();//1
三、小總結
我們在了解很多知識的時候,總會納悶這個東西的使用場景在哪里,那么閱讀完本文,你大概了解了如果讓js中函數只執行一次的三種做法,同時也結合了我的需求,對這種用法在何時使用有了一個了解。
我在文章開頭提到了angularjs的請求攔截,我發現,vue也同樣有這個玩法,要不整一篇請求攔截的文章吧。
那么本文到此結束。
四、參考