Js 防抖&节流

Posted by Youzi Blog on June 6, 2019

JS 防抖和节流


针对触发频率太高和响应很慢的问题

造成这个问题的主要原因是程序没有对事件响应做一定的限制,如果在短时间内触发频率很高,那么响应事件也会堆积很多,特别是当事件包含了网络请求,会一直向服务器发送请求,所以会希望程序不要频繁的触发响应事件。

实际应用场景

  • 监听短时间内重复触发率很高的事件,如滚动条滚动事件onscroll,窗口大小改变onresize等。

防抖(debounce)

基本策略是当事件触发时,设定一个时间周期延迟执行响应(在 JS 中可以当成是用setTimeout函数封装响应),如果在周期内响应又被触发,则重置时间周期(clearTimeout),直到周期结束,执行响应。

防抖函数有两种版本,一种是基本策略的实现,会延迟一个时间周期再执行响应事件;另一种是立即执行响应事件,再设定时间周期,如果在周期内有响应事件触发,不执行动作,且 reset 周期。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * @description: 封装防抖函数,基本策略
 * @param callback{function} 事件的回调函数
 * @param cycle{num} 防抖的时间间隔,默认500毫秒
 * @return: {function} 返回可执行函数
 */
const debounce = function (callback, cycle = 500) {
  let timer = null;
  const debounceFunc = function () {
    let _this = this,
      args = arguments;
    timer && clearTimeout(timer);
    timer = setTimeout(() => {
      callback.apply(_this, args);
    }, cycle);
  };
  return debounceFunc;
};
let fun = debounce(x => console.warn(x + 1), 1000);

fun(10);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
 * @description: 防抖函数,立即执行
 * @param callback{function} 事件回调
 * @param cycle{num} 间隔时间
 * @return: {function} 返回函数
 */
const debounce = function (callback, cycle) {
  let timer;
  const debounceFunc = () => {
    let _this = this,
      args = arguments;
    timer && clearTimeout(timer);
    let call = !timer;
    timer = setTimeout(() => {
      timer = null;
    }, cycle);
    if (call) {
      callback.apply(_this, args);
    }
  };
  return debounceFunc;
};

节流(throttle)

策略是在固定的时间周期内,只会执行响应事件一次;可以看到与防抖的区别就是,防抖会重置每次事件的间隔时间,如果连续触发,会导致第一次和第二次执行的间隔变长,而节流第一次和第二次执行的时间间隔是固定的。

节流的实现有两个版本,使用时间戳或者定时器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
 * @description: 节流函数,时间戳
 * @param callback{function}
 * @param cycle{num}
 * @return: {function}
 */
const throttle = function (callback, cycle) {
  let previous = 0;
  return function () {
    let now = Date.now();
    let _this = this,
      args = arguments;
    if (now - previous > cycle) {
      callback.apply(_this, args);
      previous = now;
    }
  };
};

/**
 * @description: 节流 定时器
 * @param callback{function}
 * @param cycle{num}
 * @return: {function}
 */
const throttle = function (callback, cycle) {
  let timer;
  return function () {
    let _this = this,
      args = arguments;
    if (!timer) {
      timer = setTimeout(() => {
        timer = null;
        callback.apply(_this, args);
      }, cycle);
    }
  };
};

区别还是比较明显的,定时器版本会在每次间隔时间的最后执行响应函数,而时间戳会在一开始就执行。

总结

防抖和节流各有特点,使用时还是要针对实际场景来应用。如果事件触发频率高,但是有一段真空期(有暂停),比如防止表单重复提交,可以使用防抖,因为肯定不会一直不间断的点提交;如果事件触发是连续的,那么可以使用节流,因为防抖会延长两次执行的时间间隔,还可能导致事件只会执行一次。