前端的节流和防抖

前端的节流和防抖

5月 16, 2021
Frontend, 系统设计

工作中排查数据问题发现了前端按钮可能会触发两次点击的情况,这个问题除了要在后端做幂等以外,正好最近我也在做一些前端的工作,也就专门研究了下前端的解决方案。本来要解决的是防抖问题,但是查了些资料发现一般都是防抖和节流一起,它们解决方案相似但又不完全相同,下面就分别介绍下方便对比使用。

防抖(debounce) #

问题描述 #

防抖很容易理解,有时如果鼠标连按,那么在很短的时间内就会触发两次点击,从而触发了两次请求。要解决这个问题,就需要保证按了鼠标的一段时间内的再次点按不能生效,或者两次点按只能生效一次。由于可能还会再连按三次,所以解决方案还是让前一次点按失效,让最后一次的点按生效。

所以防抖的解决方案是,按钮点击的 n 秒后再执行,如果 n 秒内再次触发了点击,就重新计时,通过这样的方式来保证点击的 n 秒后函数只被执行一次

解决方案 #

根据上面的思路,可以维护一个计时器变量,下面是一个简单实现:

var timer;
function debounce(fn, delay) {
    clearTimeout(timer);
    timer = setTimeout(function(){
        fn();
    }, delay);
}

这里使用了 setTimeoutclearTimeout 内置函数来实现。其中 setTimeout 的返回值是一个正整数,表示定时器的编号,这个值可以传递给 clearTimeout() 来取消该定时器。

在使用这个函数时,将 debounce 包含在实际函数外面即可:

function doSomething() {
    console.log('mounse moved');
}
document.onmousemove = () => {
    debounce(doSomething, 1000);
}

Lodash 也提供了相关的实现,可以直接使用:

jQuery(window).on('resize', _.debounce(doSomething, 150));

节流(throttle) #

问题描述 #

节流在后端的场景里很常见:当请求的并发量超出设置的流量控制策略时,只能有一部分流量能进入实际业务,其余流量被过滤掉。我之前也介绍过几个流量控制策略,前端里的流量控制策略没有那么复杂,基本就是固定窗口算法(计数法)。

所以节流要解决的问题是:每隔 n 秒的时间只执行一次函数

解决方案 #

同样是利用 setTimeout 来定义一个定时器,在 delay 毫秒以后进行函数调用同时设置 timer = null,下一次函数调用就根据 timer 是否为空来判断是否进行限流。

var timer;
function throttle(fn, delay) {
    return function () {
        var _this = this;
        var args = arguments;
        if (timer) {
            return;
        }
        timer = setTimeout(function () {
            fn.apply(_this, args);
            timer = null;
        }, delay)
    }
}

使用起来和 debounce 一样:

function doSomething() {
    console.log('mounse moved');
}
document.onmousemove = () => {
    throttle(doSomething, 1000);
}

Lodash 同样提供了方案,可以直接使用:

jQuery(window).on('scroll', _.throttle(doSomething, 100));

Lodash 为节流和防抖都提供了 cancel 方法用于取消调用,非常完善,所以绝大部分情况下还是推荐直接用 Lodash 了。

节流和防抖的区别 #

下面这个图很直观的反映了节流和防抖的区别:防抖把一段时间内的触发取最后一次按生效进行调用,基本上是期望合并后的调用产生的结果是幂等的;而节流是在每一个时间段内取一个生效的调用,多数情况下是期望每次调用的结果会不一样,所以也可以根据自己想要的结果进行选择。

防抖适用场景 #

  • 按钮,按钮一般期望只会点一次。

  • 窗口 resize,调整窗口大小一般也是期望拿到最终调整后的结果。

节流适用场景 #

  • 滚动加载,减少刷新列表的次数,优化体验和性能。

  • 搜索联想,减少因不断拼写而产生的联想次数,优化体验。

参考 #

实例解析防抖动(Debouncing)和节流阀(Throttling)(英文版)

本文共 1153 字,上次修改于 Jul 9, 2024,以 CC 署名-非商业性使用-禁止演绎 4.0 国际 协议进行许可。

相关文章

» 说说实际工作中 GraphQL 的使用体验

» 实现限流的几种方案

» Go 语言的 MPG 并发调度模型

» 权限系统通用的设计模型

» 了解下 Protobuf 相关概念