JS函数防抖和函数节流

问题引入

问题1: 如果实现了dom拖拽功能,但是在绑定拖拽事件的时候发现每当元素稍微移动一点便触发了大量的回调函数,导致浏览器直接卡死,这个时候怎么办?

问题2: 如果按钮绑定了表单提交的POST请求,但是当用户在网络不佳的时候,多次点击按钮导致表单重复提交,如何防止多次提交的发生?

针对诸如问题1的场景,可以采用函数节流,针对诸如问题2的场景,可以采用函数防抖

总的来说,两个方法都是通过时间轴上控制函数的执行次数

函数节流(throttle)

  • 概念:规定一个单位时间,在该单位时间里,只允许出现一次触发事件的回调函数执行,如果在同一单位时间内某事件被触发多次,则只有一次生效。

  • 生活中的实例: 我们知道目前的一种说法是当 1 秒内连续播放 24 张以上的图片时,在人眼的视觉中就会形成一个连贯的动画,所以在电影的播放(以前是,现在不知道)中基本是以每秒 24 张的速度播放的,为什么不是 100 张或更多?是因为 24 张就可以满足人类视觉需求的时候,100 张就会造成不必要的资源浪费

函数防抖(debounce)

  • 概念:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
  • 生活中的实例:如果有人进电梯(触发事件),那电梯将在10秒钟后出发(执行事件监听器),这时如果又有人进电梯了(在10秒内再次触发该事件),我们又得等10秒再出发(重新计时)。

分析图

假设,我们观察的总时间为10秒钟,规定1秒钟作为一次事件的最小间隔时间。

如果触发事件的频率为0.5s/次,那么

函数防抖如图

由于始终无法等待一秒钟就被再次触发,因此最终没有一次事件成功。

函数节流如图

因为控制了最多一秒一次,频率为0.5s/次,所以没过一秒钟就有一次事件作废。最终控制成1s/次

如果触发事件的频率是2s/次,那么

函数防抖如图

因为2s/次已经超过规定的最小时间,所以每计时两秒便触发一次。

函数节流如图

同样,2s/次超过了最小规定时间,因此每一次触发都生效。

应用场景

对于函数防抖,有以下几种应用场景:

  • 给按钮加函数防抖防止表单多次重复提交
  • 对于输入框连续输入进行AJAX请求时,用函数防抖可以有效减少请求次数
  • 判断scroll是否滑到底部,滚动事件+函数防抖

总的来说,防抖适合多次事件一次响应的情况

对于函数节流,有以下几种应用场景:

  • 游戏中的刷新率
  • DOM元素拖拽
  • Canvas画笔功能

总的来说,适合大量事件按时间做平均分配触发

源码示例

函数防抖:

function debounce(func,wait=300){
    let timer = 0
    return function(...args){
        if(timer) clearTimeout(timer)
        timer = setTimeout(()=>{
            func.apply(this,args)
        },wait)
    }
}
1
2
3
4
5
6
7
8
9

之所以返回一个函数,是因为防抖本身更像是一个函数修饰,所以就做了一次函数柯里化。里面也用到了闭包,闭包的变量是timer

函数节流:

function throttle(func,wait=300){
    let last_time = 0
    return function(){
        const now = Date.now()
        if(now-last_time>wait){
            last_time = now
            func.apply(this,arguments)
        }
    }
}
1
2
3
4
5
6
7
8
9
10

小结

函数防抖和函数节流是在时间轴上控制函数的执行次数。防抖可以类比为电梯不断上乘客,节流可以看做幻灯片限制频率播放电影

上次更新: 9/25/2019, 9:54:29 AM