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

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

防抖和节流

前言

最近做小程序的时候,遇到一个需求,就是连接 ble 蓝牙, 进行数据的传输。在我们的业务场景中,可能会出现频繁的数据传输,这样就会造成一些不必要的性能损耗,如果我们不做任何处理,那么会频繁的触发发送数据的操作,导致很多问题, 所以我们需要对这个过程进行一些处理,比如防抖和节流。

具体的业务场景如下:

  1. 小程序有多个选项,点击后就发送数据给蓝牙设备,要求蓝牙设备中的某条数据修改为点击的选项。
  2. 页面切换的时候,需要先发送 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,同时传递了 thisarguments,确保了函数 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);
}
};
}

总结

  • 防抖和节流都是为了减少不必要的性能损耗
  • 防抖是在一定的时间内,只能执行一次,如果时间内再次触发,则重新计时,并且只执行(最后/最先)一次触发的事件操作
  • 节流是多次执行同一个动作的时候,每隔一段时间,只执行一次, 可以一开始就执行一次,也可以最后执行一次

防抖和节流的实现原理及应用场景
https://blog.mjhxyz.top/posts/62162/
作者
Mao
发布于
2023年4月11日
许可协议