zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Leetcode 811. Subdomain Visit Count
    Leetcode 70. Climbing Stairs
    Leetcode 509. Fibonacci Number
    Leetcode 771. Jewels and Stones
    Leetcode 217. Contains Duplicate
    MYSQL安装第三步报错
    .net 开发WEB程序
    JDK版本问题
    打开ECLIPSE 报failed to load the jni shared library
    ANSI_NULLS SQL语句
  • 原文地址:https://www.cnblogs.com/codingmode/p/15407682.html
Copyright © 2011-2022 走看看