DebounceVsThrottle(防抖和节流)

本人原文

不从概念触发,先从应用讲起。如果要你设计一个输入搜索组件,你会怎么做。可能你觉得很简单,响应输入框的 keydown 和 change 事件,然后向服务器发起请求就可以了。这样的处理方案思路上是没毛病的,不过会存在两个问题,分别影响客户端和服务器。

  1. 过于频繁的响应事件,会降低用户体验。
  2. 过于频繁的请求服务器,增加服务器压力。

我们先思考一下用户增加、删除一个字符就发起请求的合理性。一般而言,搜索单个字符对于用户来说是没有意义的,用户往往是需要搜索一个单词或者短语。那么使用以上的设计,在用户输入他想要搜索的文字之前的所有搜索,都是浪费的请求。同时,过于频繁的处理事件,当事件响应变多了之后,页面会出现卡顿也是理所当然的事。另外,对于服务器而言,搜索一般来说本身就是比较复杂、耗时的服务,过多的请求不可避免地增加服务器的并发量更是给服务器增加压力。

从以上的简单分析可以知道,我们并不需要对用户的输入进行立马响应,可以等待用户输入停止之后再进行实际的逻辑处理,这样的设计也是更符合用户体验的。

这样的想法就是所谓的防抖,而另一个相似的做法是节流。他们的区别是,防抖是在最后一次事件触发后等待一定的时间再做逻辑处理,如果在这个等待时间结束之前有新的事件发生,那么这个事件就是最后一次事件,逻辑处理的时机也要相应往后延;而节流是每隔一定时间就做一次逻辑处理。他们的共同点都是降低逻辑处理的频率。

简单的防抖代码如下

function debounce(func, timespan) {
  let timer;

  return function () {
    const context = this;
    const args = arguments;

    clearTimeout(timer);
    timer = setTimeout(() => {
      func.apply(context, args);
    }, timespan);
  };
}

简单的节流代码如下

function throttle(func, timespan) {
  let tick;
  let timer;
  // 扩大context和args的范围,这样定时器每次拿到的都是最新的数据
  let context;
  let args;

  return function () {
    context = this;
    args = arguments;
    const now = Date.now().valueOf();

    if (tick) {
      if (tick + timespan < now) {
        tick = now;
        timer = setTimeout(() => {
          func.apply(context, args);
          tick = null;
        }, timespan);
      }
    } else {
      tick = now;
      timer = setTimeout(() => {
        func.apply(context, args);
        tick = null;
      }, timespan);
    }
  };
}

一张图来对比一下他们在时序上的区别(上边是 debounce,下边是 throttle,每个框等时长)

你可能感兴趣的:(DebounceVsThrottle(防抖和节流))