什么是节流?如何实现节流函数

125 阅读2分钟

节流 (throttle) 在做什么?

节流 (throttle) 指的是,在一段时间内只会执行一次触发事件的回调 (callback) 函数,若在这之中又有新事件触发,则不执行此回调函数。

在手写节流 (throttle) 函数前,我们先来了解节流的使用场景。

监听滚动事件,是搭配节流的使用场景之一。举例来说,要判断使用者是否已经滑动到页面的 30% 处,当到达时会触发一些动画效果,因此,会通过监听滚动事件时计算是否已到达该位置,但如果只要一滚动就计算会非常消耗性能,通过节流 (throttle) 可以将计算的这个回调函数在固定时间内合并一次被执行。

这里不适合使用防抖的原因是,防抖只会在事件停止被触发后的一段时间内被执行一次。因此如果用防抖,当使用者一直滑动页面,函数就永远不会被触发。这边我们仍想要函数在滑动过程中被触发,只是不想那么频繁被触发,这种场景下,节流就可以派上用场。

节流函数会接受两个参数

  • 延迟的时间 (ms)
  • 要执行的回调 (callback) 函数

手写节流 (throttle) 函数

我们先直接看代码,看看你能了解多少。有不懂的地方也不担心,下面会通过注解,一行行解释:

function throttle(fn, delay = 500) {
  let timer = null;

  return (...args) => {
    if (timer) return;
    timer = setTimeout(() => {
      fn(...args);
      timer = null;
    }, delay);
  };
}

代码注解版本和实际应用

function throttle(fn, delay = 500) {
  let timer = null;

  // throttle 本身会返回一个函数,通过 ...args 拿到该函数的变量
  return function (...args) {
    // 如果有计时器,表示还在 delay 的秒数内
    // 直接 return,不往下执行代码
    if (timer) return;

    // 如果计时器不等于 null,会进到以下逻辑

    // 设置计时器,在 delay 秒数之后,将计时器值为改为 null
    // 如果还不到 delay 的秒数,再执行一次的话
    // 因为 timer 的值不为 null,前面就先 return 不会进到这段逻辑
    // 可以达到 throttle 的目的,将一段时间内的操作,集合成一次执行
    timer = setTimeout(() => {
      timer = null;
    }, delay);

    // 直到时间到了后,timer 变成 null,才能够执行函数
    // 用 .apply 来调用 fn
    fn.apply(this, args);
  };
}

const updateThrottleText = throttle(() => {
  console.log("throttle");
}, 500);

// 如果一直滑动页面,会固定 500ms console.log('throttle')
window.addEventListener("scroll", () => {
  updateThrottleText();
});