1. 程式人生 > 其它 >axios++:防止重複提交全域性統一攔截

axios++:防止重複提交全域性統一攔截

防重提交是個老生常談的問題,使用外部變數鎖定或修改按鈕狀態的方式方式比較繁瑣冗餘,

而知乎的哥們在怎樣防止重複傳送 Ajax 請求?的問答上,提到了防重提交的幾個方式,

根據實際專案的需求,採用了A. 獨佔型提交+D. 懶惰型提交組合方式,程式碼實現如下:

// http.js

import { debounce } from "../debounce";

let pendingArr = [];
let CancelToken = axios.CancelToken;
let pendingHandler = (flag, cancelFn) => {
    if (pendingArr.indexOf(flag) > -1) {
        
if (cancelFn) { cancelFn(); // cancel } else { pendingArr.splice(pendingArr.indexOf(flag), 1); // remove flag } } else { if (cancelFn) { pendingArr.push(flag); } } }; // request interceptor axios.interceptors.request.use( config
=> { config.cancelToken = new CancelToken(cancelFn => { pendingHandler(config.baseURL + config.url + "&" + config.method, cancelFn); }); return config; }, err => { return Promise.reject(err); } ); // response interceptor axios.interceptors.response.use( response
=> { pendingHandler(response.config.url + "&" + response.config.method); return response; }, err => { pendingArr = []; return Promise.reject(err); } ); return debounce( axios(config) .then(response => { // handle response resolve(response.data) }) .catch(thrown => { if (axios.isCancel(thrown)) { console.log("Request canceled", thrown.message); } else { let { response, request, message } = thrown; reject(message); } }), 500, true );
// debounce.js
export function debounce(func, wait, immediate) {
    var timeout, args, context, timestamp, result;
    if (null == wait) wait = 100;

    function later() {
        var last = Date.now() - timestamp;

        if (last < wait && last >= 0) {
            timeout = setTimeout(later, wait - last);
        } else {
            timeout = null;
            if (!immediate) {
                result = func.apply(context, args);
                context = args = null;
            }
        }
    }

    var debounced = function() {
        context = this;
        args = arguments;
        timestamp = Date.now();
        var callNow = immediate && !timeout;
        if (!timeout) timeout = setTimeout(later, wait);
        if (callNow) {
            result = func.apply(context, args);
            context = args = null;
        }

        return result;
    };

    debounced.clear = function() {
        if (timeout) {
            clearTimeout(timeout);
            timeout = null;
        }
    };

    debounced.flush = function() {
        if (timeout) {
            result = func.apply(context, args);
            context = args = null;

            clearTimeout(timeout);
            timeout = null;
        }
    };

    return debounced;
}

這裡用到了 axios 攔截器,初始化一個pendingArr陣列,用於存放正在 pending 的請求,以url+method為標誌,

在 http response 攔截器刪除完成 pending 的請求,在 http request 的攔截器中,先判斷是否存在正在 pending 的同一個請求,若無則把請求標誌位存入陣列,若存在,則取消請求。

到這裡還未結束,前面做的是終止請求,但是請求已經發出,短時間內頻繁請求,會對伺服器造成一定壓力,

所以我這裡用了一個debounce (防抖動),規定時間內把觸發非常頻繁的事件合併成一次執行,比如我這裡是500ms 內,觸發很頻繁的請求都會合併成一次執行,避免使用者瘋狂點選,觸發多次請求的情況。

至於debounce的實現,我這裡是摘取underscore原始碼,至於原理可以參考Debouncing and Throttling Explained Through Examples,這裡闡述了debounce (防抖動)throttling(節流閥)的原理及其異同。

簡化版:

let pending = []; //宣告一個數組用於儲存每個ajax請求的取消函式和ajax標識
let cancelToken = axios.CancelToken;
let removePending = (config) => {
    for(let p in pending){
        if(pending[p].u === config.url + '&' + config.method) { //噹噹前請求在陣列中存在時執行函式體
            pending[p].f(); //執行取消操作
            pending.splice(p, 1); //把這條記錄從陣列中移除
        }
    }
}
 
//新增請求攔截器
axios.interceptors.request.use(config=>{
     removePending(config); //在一個ajax傳送前執行一下取消操作
     config.cancelToken = new cancelToken((c)=>{
        // 這裡的ajax標識我是用請求地址&請求方式拼接的字串,當然你可以選擇其他的一些方式
        pending.push({ u: config.url + '&' + config.method, f: c });  
    });
     return config;
   },error => {
     return Promise.reject(error);
   });
 
//新增響應攔截器
axios.interceptors.response.use(response=>{
      removePending(res.config);  //在一個ajax響應後再執行一下取消操作,把已經完成的請求從pending中移除
      return response;
   },error =>{
      return { data: { } }; 返回一個空物件,否則控制檯報錯
   });

引:https://www.cnblogs.com/Sabo-dudu/p/12457186.html