前端控制重复提交

#问题再现: 在写看点账号和关键词监控的微博文章部分时,有一个上拉加载的功能,基本实现后在测试时发现:每当下拉的过快或者做重复下拉的操作,ajax就会请求不到数据并返回canceled的状态码。

#原因探究 经过排查,ajax请求被canceled是因为在ajax请求资源的时间内,后续的操作继续触发了ajax请求资源,因此请求操作被取消,所以解决问题关键就是如何防止重复发送ajax请求。

#解决方法 ##1 节制型提交(throttle) 无论提交如何频繁,任意两次有效提交的间隔时间必定会大于或等于某一时间间隔;即以一定频率提交。

module.submit = throttle(150, function() {
  // todo
})

throttle释义:

throttle在英文中是节流阀的意思,顾名思义,就像原本一直流水的龙头,现在每隔一定时间开一下,以节流。也就是会说预先设定一个执行周期,当调用动作的时刻大于等于执行周期则执行该动作,然后进入下一个新周期。

使用场景: 鼠标移动,mousemove 事件;DOM 元素动态定位,window对象的resize和scroll 事件等。

demo:

    window.addEventListener("resize", throttle(callback, 3000, {leading:true}));
    window.addEventListener("resize", throttle(callback2, 0, {leading:false}));
    function callback ()  { console.count("Throttled"); }
    function callback2 () { console.count("Not Throttled"); }
    /**
    * 频率控制函数, fn执行次数不超过 1 次/delay
    * @param fn{Function}     传入的函数
    * @param delay{Number}    时间间隔
    * @returns {Function}     返回调用函数
    */
    function throttle(fn,delay,options) {
         var wait=false;
        if (!options) options = {};
        return function(){
            var that = this;
             if(!wait){
                 if (options.leading === false){
                     // 非截流操作
                     fn.apply(that)
                    }
                 else { 
                    // 截流操作
                    wait=true;
                    setTimeout(function () {
                       fn.apply(that);
                       wait=false;
                    },delay);
                }
            }
        }
    }

##2 懒惰型提交(debounce) 任意两次提交的间隔时间,必须大于一个指定时间,才会促成有效提交;即不给休息不干活。

module.submit = debounce(150, function() {  
  // todo
})

debounce释义:

bounce是名词弹力,或者动词反弹的意思, de-表否定/相反的前缀,形象低表示一直持续低压抑住不让反弹,当然最后还是得松手,那么反弹一次。 也就是说当调用动作n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间。

使用场景: 文本输入keydown 事件,keyup 事件等。

扫描二维码关注公众号,回复: 5517755 查看本文章

demo:

    var resizeTimer=null;
    $(window).on('resize',function(){
           if(resizeTimer){
               clearTimeout(resizeTimer)
           }
           resizeTimer=setTimeout(function(){
               console.log("window resize");
           },1000);
       }
    );

throttle和debounce的区别理解 福兮,祸之所倚;祸兮,福之所伏。 ——《老子》

##二者的不同之处: throttle 可以想象成阀门一样定时打开来调节流量。 debounce可以想象成把很多事件压缩组合成了一个事件。

有个生动的类比,假设你在乘电梯,每次进一个人需等待10秒钟,不考虑电梯容量限制,那么两种不同的策略是:

debounce 你在进入电梯后发现这时不远处走来了一个人,等10秒钟,这个人进电梯后不远处又有个妹纸姗姗来迟,怎么办,再等10秒,于是妹纸上电梯时又来了一对好基友...,作为感动中国好码农,你要每进一个人就等10秒,直到没有人进来,10秒超时,电梯开动。

throttle 电梯很忙,每次就只等10秒,不管是来了妹纸还是好基友,电梯每隔10秒准时送一波人。

因此,简单来说,debounce适合只执行一次的情况,例如 搜索框中的自动完成。在停止输入后提交一次ajax请求; 而throttle适合指定每隔一定时间间隔内执行不超过一次的情况,例如拖动滚动条,移动鼠标的事件处理等。

旁礴万物以为一。 ——《逍遥游》

##二者的相同在于:

首先我们的直观感觉是使用 debounce 方法相比于 throttle 方法事件触发的频率更低,但实际上不能这么理解。首先需要了解 debounce 和 throttle 的原理。

当我们阅读lodash,underscore等js工具库对这两种方法的代码实现时会发现,throttle 方法不过是 debounce 方法的一个修饰。也就是说,.throttle和.debounce最终都会都会调用 debounce 方法。那么 debounce 究竟是如何工作的呢? 首先看 _.debounce 的 API: _.debounce(func, delay, options);

func 是需要被调用的目标函数,delay 是时间限制,options 是一系列的配置,为了简化问题,这里不多提。

当调用 *_.debounce *后,会返回一个函数,这个函数在被调用时会生成一个 setTimeout(delayed, delay)。其中 delayed 又是一个内部方法,在 delayed 被调用时进行如下检测:当前时间 - 上次func被调用事件 是否 小于 0 或 大于 delay ?如果是则执行一次 func,记录并返回执行结果,同时更新上次被调用时间;如果不是则调用 setTimeout 进行下一次的判断。

因此,当你像下面这样绑定事件,

$(window).on('mousemove', _.debounce(moveHandler, 500));

并频繁移动鼠标时,你会发现 moveHandler 压根没有被调用过!直到你停下来后,moveHandler 才会被调用一次。

至于_.throttle方法,只不过是多给 debounce 传了一个 maxWait 选项,这个选项的意思是至少保证在每 maxWait 时间让 func 被调用一次。

说到这儿,我们就明白了为什么会出现上图那样的调用情况。如果你频繁的移动鼠标,throttle 会保证在每 maxWait 时间调用 func 一次,而 debounce 如果没有明确设置 maxWait,是一直不会调用 func 直到你停止移动鼠标后才会调用一次。

猜你喜欢

转载自blog.csdn.net/qq_38930240/article/details/87975779