首页 > 技术文章 > 函数防抖和函数节流的概念及原理

SallyShan 2022-02-18 16:02 原文

概念

函数防抖(debounce)

函数防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

简单的说,当一个动作连续触发,则只执行最后一次

打个比方,坐公交,司机需要等最后一个人进入才能关门。每次进入一个人,司机就会多等待几秒再关门。

函数节流(throttle)

限制一个函数在一定时间内只能执行一次。

举个例子,乘坐地铁,过闸机时,每个人进入后3秒后门关闭,等待下一个人进入。

为了方便理解,我们首先通过一个可视化的工具,感受一下三种环境(正常情况、函数防抖情况 debounce、函数节流 throttle)下,对于mousemove事件回调的执行情况。

 
三种环境下,mousemove事件执行分布图

 

 

 

竖线的疏密代表事件执行的频繁程度。可以看到,正常情况下,竖线非常密集,函数执行的很频繁。而debounce(函数防抖)则很稀疏,只有当鼠标停止移动时才会执行一次。throttle(函数节流)分布的较为均已,每过一段时间就会执行一次。

常见应用场景

函数防抖的应用场景

连续的事件,只需触发一次回调的场景有:

  • 搜索框搜索输入。只需用户最后一次输入完,再发送请求
  • 手机号、邮箱验证输入检测
  • 窗口大小Resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。

函数节流的应用场景

间隔一段时间执行一次回调的场景有:

  • 滚动加载,加载更多或滚到底部监听
  • 谷歌搜索框,搜索联想功能
  • 高频点击提交,表单重复提交

实现原理

函数防抖(debounce)

函数防抖的简单实现:

const _.debounce = (func, wait) => {
  let timer;

  return () => {
    clearTimeout(timer);
    timer = setTimeout(func, wait);
  };
};

函数防抖在执行目标方法时,会等待一段时间。当又执行相同方法时,若前一个定时任务未执行完,则 clear 掉定时任务,重新定时。

函数节流(throttle)

1)函数节流的 setTimeout 版简单实现

const _.throttle = (func, wait) => {
  let timer;

  return () => {
    if (timer) {
      return;
    }

    timer = setTimeout(() => {
      func();
      timer = null;
    }, wait);
  };
};

函数节流的目的,是为了限制函数一段时间内只能执行一次。因此,通过使用定时任务,延时方法执行。在延时的时间内,方法若被触发,则直接退出方法。从而,实现函数一段时间内只执行一次。

2)函数节流的时间戳版简单实现
根据函数节流的原理,我们也可以不依赖 setTimeout实现函数节流。

const throttle = (func, wait) => {
  let last = 0;
  return () => {
    const current_time = +new Date();
    if (current_time - last > wait) {
      func.apply(this, arguments);
      last = +new Date();
    }
  };
};

其实现原理,通过比对上一次执行时间与本次执行时间的时间差与间隔时间的大小关系,来判断是否执行函数。若时间差大于间隔时间,则立刻执行一次函数。并更新上一次执行时间。

异同比较

相同点:

  • 都可以通过使用 setTimeout 实现。
  • 目的都是,降低回调执行频率。节省计算资源。

不同点:

  • 函数防抖,在一段连续操作结束后,处理回调,利用 clearTimeout 和 setTimeout 实现。函数节流,在一段连续操作中,每一段时间只执行一次,频率较高的事件中使用来提高性能。
  • 函数防抖关注一定时间连续触发,只在最后执行一次,而函数节流侧重于一段时间内只执行一次。



推荐阅读