性能优化: 防抖和节流

264 阅读3分钟

debounce 防抖

debounce(f, ms)装饰器的结果是一个包装器,该包装器将暂停对 f 的调用,直到经过 ms 毫秒的非活动状态(没有函数调用,“冷却期”),然后使用最新的参数调用 f 一次。

换句话说,debounce 就像一个“接听电话”的秘书,并一直等到 ms 毫秒的安静时间之后,才将最新的呼叫信息传达给“老板”(调用实际的 f)。

举个例子,我们有一个函数 f,并将其替换为 f = debounce(f, 1000)

然后,如果包装函数分别在 0ms、200ms 和 500ms 时被调用了,之后没有其他调用,那么实际的 f 只会在 1500ms 时被调用一次。也就是说:从最后一次调用开始经过 1000ms 的冷却期之后。

debounce.png

……并且,它将获得最后一个调用的所有参数,其他调用的参数将被忽略。

防抖举例

现在我们举一个实际中的例子。假设用户输入了一些内容,我们想要在用户输入完成时向服务器发送一个请求。

我们没有必要为每一个字符的输入都发送请求。相反,我们想要等一段时间,然后处理整个结果。

Web 浏览器中,我们可以设置一个事件处理程序 —— 一个在每次输入内容发生改动时都会调用的函数。通常,监听所有按键输入的事件的处理程序会被调用的非常频繁。但如果我们为这个处理程序做一个 1000msdebounce 处理,它仅会在最后一次输入后的 1000ms 后被调用一次。

debounce.gif

看到了吗?第二个输入框调用了防抖函数,所以它的内容是在最后一次输入的 1000ms 后被处理的。

因此,debounce 是一个处理一系列事件的好方法:无论是系列键盘输入,鼠标移动还是其他类似的事件。

它在最后一次调用之后等待给定的时间,然后运行其可以处理结果的函数。

实现一个防抖函数

function debounce(f, ms) {
  let timeout;
  return function() {
    clearTimeout(timeout);
    timeout = setTimeout(() => f.apply(this, arguments), ms);
  };
}

调用 debounce 会返回一个包装器。当它被调用时,它会安排一个在给定的 ms 之后对原始函数的调用,并取消之前的此类超时。

throttle节流

当被多次调用时,它会在每 ms 毫秒最多将调用传递给 f 一次。

与防抖抖的不同是,它是个完全不同的装饰器:

debounce 会在“冷却”期后运行函数一次。适用于处理最终结果。 throttle 运行函数的频率不会大于所给定的时间 ms 毫秒。适用于不应该经常进行的定期更新。

换句话说,throttle 就像接电话的秘书,但是打扰老板(实际调用 f)的频率不能超过每 ms 毫秒一次。

节流举例

throttle.gif

实现一个节流函数

    function throttle(f, ms) {
      let timer = null;
      return function () {
       if (timer) return
       timer = setTimeout(() => {
          f,apply(this, arguments);
          timer = null
        }, ms)
      }
    }

总结

防抖:用户输入结束或暂停时,才会触发change事件

节流:无论输入速度多快,每隔一定时间只会触发一次