防抖和节流的实现原理及应用场景

前言
最近做小程序的时候,遇到一个需求,就是连接 ble 蓝牙, 进行数据的传输。在我们的业务场景中,可能会出现频繁的数据传输,这样就会造成一些不必要的性能损耗,如果我们不做任何处理,那么会频繁的触发发送数据的操作,导致很多问题, 所以我们需要对这个过程进行一些处理,比如防抖和节流。
具体的业务场景如下:
- 小程序有多个选项,点击后就发送数据给蓝牙设备,要求蓝牙设备中的某条数据修改为点击的选项。
- 页面切换的时候,需要先发送 ble 数据,提示蓝牙设备需要传某些数据到小程序中
什么是防抖和节流
防抖: 就是在规定的时间内,只能执行一次,如果时间内再次触发,则重新计时,并且只执行最后一次触发的事件操作
- 防抖适用于频繁触发的事件,比如搜索框输入,提交等
- 如果多次触发,那么只会执行
最后一次
节流: 就是多次执行同一个动作的时候,每隔一段时间,只执行一次
- 节流使用于连续触发的事件, 比如滚动加载等
每隔一定时间执行一次事件的处理函数
防抖和节流的实现
防抖
1 2 3 4 5 6 7 8 9 10 11 12
| function debounce(fn, delay) { let timer = null; return function () { let context = this; let args = arguments;
clearTimeout(timer); timer = setTimeout(function () { fn.apply(context, args); }, delay); }; }
|
- 接收两个参数,第一个是需要执行的函数,第二个是延迟时间
- 通过
闭包来保存了需要执行的函数 fn 和延迟时间 delay
- 通过
clearTimeout() 方法清除上一次的定时器, 保证只有一个定时器在执行
- 通过
setTimeout() 方法来创建一个新的定时器,用来在延迟时间之后执行需要执行的函数 fn
- 定时器,通过
apply() 方法来调用需要执行的函数 fn,同时传递了 this 和 arguments,确保了函数 fn 的执行环境和参数正确
使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| export default { name: 'ThrottlingDebouncing', data() { return { count: 0 } }, created() { this.addCount = debounce(this._addCount, 1000); }, methods: { _addCount() { this.count++ }, }, }
|
后续也可以更具实际的业务场景进行适当的调整,比如说防抖会使用最后一次调用的参数,也可以改成使用第一次调用的参数等等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function debounce(fn, delay) { let timer = null; return function () { let context = this; let args = arguments;
if (!timer) { fn.apply(context, args); } clearTimeout(timer); timer = setTimeout(function () { timer = null; }, delay); }; }
|
节流
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function throttle(fn, delay) { let timer = null; return function () { let context = this; let args = arguments;
if (!timer) { timer = setTimeout(function () { fn.apply(context, args); timer = null; }, delay); } }; }
|
- 和防抖的实现原理类似, 也是通过
闭包 来保存需要执行的函数 fn 和延迟时间 delay
- 没有定时器的时候, 就创建一个定时器, 在定时器的回调函数中执行需要执行的函数
fn, 并且清空定时器
使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| export default { name: 'ThrottlingDebouncing', data() { return { count: 0 } }, created() { this.addCount = throttle(this._addCount, 1000); }, methods: { _addCount() { this.count++ }, }, }
|
也可以像防抖一样,根据实际的业务场景进行适当的调整,比如说节流会在 delay 后才执行,也可以改为立即先执行一次。我切换页面的时候,就需要立即执行一次,所以我就使用了下面的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function throttle(fn, delay) { let timer = null; return function () { let context = this; let args = arguments;
if (!timer) { fn.apply(context, args); timer = setTimeout(function () { timer = null; }, delay); } }; }
|
总结
- 防抖和节流都是为了减少不必要的性能损耗
防抖是在一定的时间内,只能执行一次,如果时间内再次触发,则重新计时,并且只执行(最后/最先)一次触发的事件操作
节流是多次执行同一个动作的时候,每隔一段时间,只执行一次, 可以一开始就执行一次,也可以最后执行一次