目录

  1. 防抖(debounce)
  2. 节流(throttle)
  3. 防抖和节流的区别

上周写了个表单,没仔细看Toast的遮罩配置,以为和之前一样都是默认开启的,然而实际上是被关了,又因为我偷懒没给按钮加防抖节流,于是出现了用户重复提交多条数据的情况…

Android防止抖动 安卓防抖软件_.net

默默掏出小本本记下究竟谁关了我的遮罩按钮

好了,言归正传,来复习下防抖节流。

1、防抖

防抖,debounce。这个概念最早来自于相机,现在流行的相机防抖原理也分很多种,有镜头防抖、机身防抖等,我们代码里的防抖比较类似软件防抖:

“软件会持续地监测手的抖动情况:如果你的手正在抖动,这时候即便你已经摁下了快门手机也不会进行拍照,直到它监测到你忽然有一个瞬间手是比较静止的,诶!它就自动按下了快门!所以这个并不是什么主动的防抖功能,它只是主动选择了一个’手不抖的时间’拍照。这个功能适合拍风光,不适合拍人像,因为它没有办法抓拍。”[1]

Android防止抖动 安卓防抖软件_javascript_02

再举个生活中的防抖例子,坐过电梯的童鞋应该感受过,电梯总是要等一小段时间后再关门,如果在电梯门关上之前,又有人进入,那么电梯就会重新开门等一小段时间再关上。如果不断有人在电梯关闭前进入,电梯就会等最后一个人进来后,再等待一小段时间后才真正关门运行。

所谓防抖,就是在事件触发后延迟n秒再执行;如果在这n秒内再次触发该事件,则重新计时,延迟n秒后再执行。

Android防止抖动 安卓防抖软件_事件触发_03

代码实现:

function debounce(fn, delay) {
    let timer = null;
    return () = >{
        if (timer) {
            // 重新计时
            clearTimeout(timer);
        }
        const context = this;
        timer = setTimeout(() = >{
            fn.apply(context, arguments);
            clearTimeout(timer);
        },
        delay);
    }
}


let count = 0;
const play = debounce(() = >{
    console.log(count++);
},
3000);

在线运行:http://jsrun.net/Tr8Kp/edit

节流

节流,throttle。工程热力学术语释义:管道中流动的流体经过通道截面突然缩小的阀门、狭缝及孔口等部分后发生压力降低的现象称为节流。[2]

Android防止抖动 安卓防抖软件_Android防止抖动_04

简单来讲,就是通过阀门限制实际流量。比如用户在10s内发起了1000次请求,节流控制后,实际上10s内,只有1次请求会被真正发送到服务器,其他请求都不会发出。

节流就是一段时间内,不论事件触发多少次,都只执行一次。

Android防止抖动 安卓防抖软件_事件触发_05

代码实现:

// 时间戳版本
function throttle(fn, delay) {
    let isRun = false;
    let startTime = Date.now();
    return function() {
        let now = Date.now();
        if (now - startTime > delay) {
            // 延迟执行
            // const context = this
            // fn.apply(context, arguments)
            startTime = now isRun = false;
        }


        if (isRun) return;


        isRun = true;


        // 立即执行
        const context = this 
        fn.apply(context, arguments)
    }
}

let count = 0;
const play = throttle(() = >{
    console.log(count++);
},
3000);

在线运行:http://jsrun.net/Wr8Kp/edit

// 定时器版本
function throttle(fn, delay) {
    let timer = null
    return () = >{
        if (timer) return;
        const context = this;
        // 立即执行
        fn.apply(context, arguments);
        timer = setTimeout(() = >{
            // 延迟执行
            // fn.apply(context, arguments);
            clearTimeout(timer);
            timer = null;
        },
        delay);
    }
}


let count = 0;
const play = throttle(() = >{
    console.log(count++);
},
3000);

在线运行:http://jsrun.net/Lr8Kp/edit

防抖和节流的区别

看看下面这段测试代码:

let t = 0;
/** 每隔一秒点一下,一直都不会触发,除非停止自动点击 */
let interval = null;
start();
function start() {
    if (!interval) {
        interval = setInterval(() = >{
            t++;
            console.log("触发" + t + "次");
            play();
        },
        1000);
    }
}


function stop() {
    console.log("点击了停止触发");
    clearInterval(interval);
    interval = null;
}

这段测试代码,每隔1秒会触发一次play()方法,按照上面防抖和节流的代码,delay都是3s的话,输出结果各是什么?

防抖debounce输出结果:

Android防止抖动 安卓防抖软件_javascript_06

节流throttle输出结果:

Android防止抖动 安卓防抖软件_javascript_07

区别:

  • 防抖因为一直不断被触发,每次触发都会重新计时,始终达不到3s的延迟,所以始终无法执行;只有在停止触发后才会执行(最后一次触发的3s后,输出count值0);
  • 而节流则每隔3s执行一次;3s内可能有多次触发,但这3s内只会执行一次。