欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

节流(Throttle)和防抖(Debounce)傻傻分不清

程序员文章站 2022-05-29 11:34:29
...

这两个概念有时候傻傻分不清,但实际工作中,相信只要需求足够清楚后,大家也都能实现这种逻辑控制,只是不知道这些术语罢了,就像“闭包”(有些人看到这个词时,可能又要自我解释和纠结了...)。但这些术语既然已经产生了,而且还在面试中被经常提起,那我们还是需要来文绉绉一回,参考一些文章,做个梳理。

节流概念(Throttle)

从上一次命令结束开始的一定时间范围t内,如果多次连续下达命令,则只执行当前时间段t内第一次命令。
最终效果:对于连续动作,会过滤出部分动作,让这些过滤后的动作之间的执行间隔大于等于t

按照设定的时间固定执行一次函数,比如200ms一次。注意:固定就是你在mousemove过程中,执行这个节流函数,它一定是200ms(你设定的定时器延迟时间)内执行一次。没到200ms,一定会返回,没有执行回调函数的。

主要应用场景有:scroll、touchmove

范例

let gapTime = 1000 
let lastTime = null
let nowTime = null
let fn = () => console.log('我执行了')
document.body.onscroll = () => {
    nowTime = Date.now() 
    if(!lastTime || nowTime - lastTime > gapTime) {
        fn()
        lastTime = nowTime
    }

}

封装

首次不执行(仔细体会这种首次不执行的节流和下文中首次不执行的防抖之间的区别):

function throttle(fn, gapTime) {
    let timer = null
    return function() {
        if (time) {
            return;
        }
        timer = setTimeout(() => {
            console.log('我执行了')
        }, 1000)
    }
}

首次执行:

function throttle(fn, gapTime) {
    let lastTime = null
    let nowTime = null
    return function() {
        nowTime = Date.now()
        if (!lastTime || nowTime - lastTime > gapTime) {
            fn()
            lastTime = nowTime
        }
    }
}

使用方法:

let fn = () => console.log('我执行了')
fn = throttle(fn, 1000)

document.body.onscroll = fn

防抖概念(Debounce)

如果下达该命令后,在t毫秒内再次下达该命令,则取消刚刚下达的命令,只执行新命令
最终效果: 对于连续动作(动作间的时间间隔小于t),以最后一次为准

抖动停止后的时间超过设定的时间时执行一次函数。注意:这里的抖动停止表示你停止了触发这个函数,从这个时间点开始计算,当间隔时间等于你设定时间,才会执行里面的回调函数。如果你一直在触发这个函数并且两次触发间隔小于设定时间,则一定不会到回调函数那一步。

主要应用场景有:input验证、搜索联想、resize

范例

let timer 
document.body.onscroll = () => {
    console.log('这里每次都执行')
    if (timer) {
        clearTimeout(timer)
    }
    timer = setTimeout(() => {
        console.log('这里只执行很少次')
    }, 1000)
}
封装

首次不执行,一般可以满足需求了:

function debounce(fn, wait) {
    let timer = null
    return function() {
        if(timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(() => fn.apply(this, arguments), wait)
    }
}

首次执行,较为复杂,请参考底部链接中的文章。

使用方法:

let fn = () => console.log('这里只执行很少次')
fn = debounce(fn, 1000)

document.body.onscroll = fn

白话文

1. 节流

正如字面意思,当液体(事件)连续流动(触发)时,我们介入这个过程中是为了节约资源。想象一个容量超大,但底部装有阀门的漏斗,尽管上方持续向漏斗中倒水,但底部的阀门却是每隔一段时间才会打开一次,然后又立刻关闭。因此在漏斗的出水口,连续的液体却被控制成了一滴一滴的下落。

类似地,当如滚动这种事件持续触发时,我们不希望那么密集的触发回调函数,浪费资源,就可以使用节流,单位时间只能触发一次调用,如果从页面顶部滚动到底部的过程中经过了10个单位时间, 那么就只会触发10次调用。

2. 防抖

假设你想要迈出步子向前走,但是你可能抖腿抖了一上午,你的脚每次刚抬起就又落下,始终在快速抬起和落下之间切换,并没有任何前进。而防抖的意思就像是一种规定,你可劲儿地抖吧,啥时候不抖了,你也就实质性的迈出了这一步(函数得到了一次调用)。

(有兴趣的可以专门查一下 Throttle 和 Debounce 的单词释义)


参考:
1. JS节流和防抖的区分和实现详解