• 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

  • 相关阅读:
    (24)码表
    (23)IO之打印流 PrintStream & Printwriter
    (22)Properties,这不会Io流中的类,但是通常和IO流中的一些流配合使用
    (21)IO流之对象的序列化和反序列化流ObjectOutputStream和ObjectInputStream
    (20)IO流之SequenceInputStream 序列流
    01.vue基础
    02.1插值操作-Mustache语法
    jQuery对节点的增删改查和一些常用的逻辑处理
    递归 阶乘
    电脑远程连接
  • 原文地址:https://www.cnblogs.com/codingmode/p/15407682.html
Copyright © 2020-2023  润新知