节流与防抖

防抖和节流是针对响应跟不上触发频率这类问题的两种解决方案。在给DOM绑定事件时,有些事件我们是无法控制触发频率的。 如鼠标移动事件onmousemove, 滚动滚动条事件onscroll,窗口大小改变事件onresize,这类操作都会导致事件会被高频触发。如果事件的回调函数较为复杂,就会导致响应跟不上触发,出现页面卡顿,假死现象。

又比如,在实时检查输入时,如果我们绑定onkeyup事件发请求去服务端检查,用户输入过程中,事件的触发频率也会很高,会导致大量的请求发出,响应速度会大大跟不上触发。

在滚动事件中需要做个复杂计算或者实现一个按钮的防二次点击操作。这些需求都可以通过函数防抖动来实现。
尤其是第一个需求,如果在频繁的事件回调中做复杂计算,很有可能导致页面卡顿,不如将多次计算合并为一次计算,只在一个精确点做操作。

防抖和节流的作用都是防止函数多次调用

防抖

debounce,去抖动。
debounce基本思想:当事件被触发时,设定一个周期延迟执行,若期间又被触发,则重新设定周期,直到周期结束,执行动作。
这意味着,在一个周期内,狂点的话,每次点击都会重新倒计时。因此,最后一次点击后,再等待一个周期的时间,才会执行。
如图:
300

后期,又扩展了前缘debounce,即执行动作在前,然后设定周期,周期内有事件被触发,不执行动作,且周期重新设定。
也就是说,在一个周期内,狂点的话,第一次点击就会马上执行,后续的所有点击都不会执行动作,只会重新倒计时,最后更新的倒计时结束之后,会清空计时器。
300

这个防抖只能在最后调用。一般的防抖会有immediate选项,表示是否立即调用。
1. 在搜索引擎搜索的时候,显然希望用户输入完最后一个字才调用查询接口,此时适用延迟执行的防抖函数,它总是在一连串(间隔小于wait的)函数触发之后调用。
2. 用户在github点star的时候,在用户点第一下的时候就要去调用接口,并且成功之后改变star按钮的样子,此时适用立即执行的防抖函数,它总是在第一次调用,并且下一次调用必须与前一次调用的时间间隔大于wait才会触发。

优化后,带有立即执行选项的防抖函数。如下:

对于按钮防二次点击的实现:
如果函数是立即执行的,就立即调用;
如果函数是延迟执行的,就缓存上下文和参数,放到延迟函数中去执行。开始一个定时器后,只要定时器还在,每次点击都重新计时。等定时器时间到,定时器重置为 null,就可以再次点击了。

debounce的其他版本:

节流

节流的策略是,固定周期内,只执行一次动作,若有新事件触发,不执行。周期结束后,又有事件触发,开始新的周期。
特点是:在连续高频触发事件时,动作会被定期执行,响应平滑。
节流策略也分前缘和延迟两种。延迟是指周期结束后执行动作,前缘是指执行动作后再开始周期。

延迟的节流示意图:
300

前缘的节流示意图:
300

其他版本的throttle

总结

debouncethrottling各有特点,要根据具体情况进行选择。如果事件触发是高频但是有停顿时,可以选择debounce; 在事件连续不断高频触发时,只能选择throttling,因为debounce可能会导致动作只被执行一次,界面出现跳跃。