首页 > web前端 > js教程 > 正文

去抖和节流

王林
发布: 2024-08-09 20:32:30
原创
439 人浏览过

Debouncing and Throttling

另一个流行的前端面试问题。它测试受访者对 JS、性能和 FE 系统设计的知识。

这是前端面试问题系列的第二个问题。如果您希望提高准备水平或保持最新状态,请考虑注册 FrontendCamp。


去抖和节流的工作原理相同 - 延迟东西 - 但仍然有非常不同的方法和用例。

这两个概念对于开发高性能应用程序都很有用。 您每天访问的几乎所有网站都以某种方式使用去抖和节流。

去抖

去抖动的一个众所周知的用例是预先输入(或自动完成)。

假设您正在为一个拥有数千种产品的电子商务网站构建搜索功能。当用户尝试搜索某些内容时,您的应用将进行 API 调用来获取与用户的查询字符串匹配的所有产品。

const handleKeyDown = async (e) => {
 const { value } = e.target;
 const result = await search(value);
 // set the result to a state and then render on UI
}

<Input onKeyDown={handleKeyDown} />
登录后复制

这种方法看起来不错,但有一些问题:

  1. 您正在对每个按键事件进行 API 调用。如果用户键入 15 个字符,则单个用户会调用 15 次 API。这永远无法扩展。
  2. 当这 15 个 API 调用的结果到达时,您只需要最后一个。之前 14 次调用的结果将被丢弃。它会消耗大量用户的带宽,慢速网络上的用户会看到明显的延迟。
  3. 在 UI 上,这 15 个 API 调用将触发重新渲染。它会使组件变得滞后。

解决这些问题的方法是去抖动。

基本思想是等到用户停止输入。我们将延迟 API 调用。

const debounce = (fn, delay) => {
 let timerId;
 return function(...args) {
  const context = this;

  if (timerId) {
    clearTimeout(timerId);
  };
  timerId = setTimeout(() => fn.call(context, ...args), delay);
 }
}

const handleKeyDown = async (e) => {
 const { value } = e.target;
 const result = await search(value);
 // set the result to a state and then render on UI
}

<Input onKeyDown={debounce(handleKeyDown, 500)} />
登录后复制

我们扩展了现有代码以利用去抖动。

去抖动函数是通用实用函数,它接受两个参数:

  1. fn:应该延迟的函数调用。
  2. 延迟:以毫秒为单位的延迟。

在函数内部,我们使用 setTimeout 来延迟实际的函数(fn)调用。如果在计时器耗尽之前再次调用 fn,计时器将重置。

通过我们更新的实现,即使用户输入 15 个字符,我们也只会进行 1 次 API 调用(假设每次按键的时间少于 500 毫秒)。这解决了我们开始构建此功能时遇到的所有问题。

在生产代码库中,您不必编写自己的去抖动实用函数。您的公司很可能已经使用了像 lodash 这样具有这些方法的 JS 实用程序库。

节流

嗯,去抖对于性能来说非常有用,但在某些情况下,我们不想在收到更改通知之前等待 x 秒。

想象一下您正在构建一个协作工作空间,例如 Google Docs 或 Figma。关键功能之一是用户应该实时了解其他用户所做的更改。

到目前为止我们只知道两种方法:

  1. Noob 方法:每当用户移动鼠标指针或键入内容时,都会进行 API 调用。你已经知道事情会变得多么糟糕。
  2. 去抖动方法:它确实解决了性能方面的问题,但从用户体验的角度来看,它很糟糕。您的同事可能会写一个 300 字的段落,但您最终只会收到一次通知。还算实时吗?

这就是节流的用武之地。它位于上述两种方法的中间。基本思想是 - 定期通知 - 不是在最后也不是在每次按键时通知,而是定期通知。

const throttle = (fn, time) => {
 let lastCalledAt = 0;

 return function(...args) {
  const context = this;
  const now = Date.now();
  const remainingTime = time - (now - lastCalledAt);

  if (remainingTime <= 0) {
   fn.call(context, ...args);
   lastCalledAt = now;
  }
 }
}

const handleKeyDown = async (e) => {
 const { value } = e.target;
 // save it DB and also notify other peers
 await save(value);
}

<Editor onKeyDown={throttle(handleKeyDown, 1000)} />
登录后复制

我们修改了现有代码以利用节流功能。它需要两个参数:

  1. fn:实际的节流函数。
  2. time:允许执行该函数的时间间隔。

实施非常简单。我们将函数上次调用的时间存储在lastCalledAt中。下次,当进行函数调用时,我们检查时间是否已经过去,然后才执行 fn。

我们已经快到了,但是这个实现有一个错误。如果最后一次使用某些数据的函数调用是在时间间隔内进行的,并且此后没有进行任何调用,该怎么办?根据我们当前的实现,我们将丢失一些数据。

为了解决这个问题,我们将参数存储在另一个变量中,并启动一个超时,以便在没有收到事件时稍后调用。

const throttle = (fn, time) => {
 let lastCalledAt = 0;
 let lastArgs = null;
 let timeoutId = null;

 return function(...args) {
  const context = this;
  const now = Date.now();
  const remainingTime = time - (now - lastCalledAt);

  if (remainingTime <= 0) {
   // call immediately
   fn.call(context, ...args);
   lastCalledAt = now;
   if (timeoutId) {
     clearTimeout(timeoutId);
     timeoutId = null;
   }
  } else {
    // call later if no event is received
    lastArgs = args;
    if (!timeoutId) {
      timeoutId = setTimeout(() => {
        fn.call(context, ...lastArgs);
        lastCalledAt = Date.now();
        lastArgs = null;
        timeoutId = null;
      }, remainingTime);
    }
  }
 }
}
登录后复制

此更新的实现可确保我们不会错过任何数据。

Lodash 还提供了节流实用功能。


概括

  1. 去抖动和节流是性能优化技术。
  2. 这两者的工作原理相似 - 延迟事情。
  3. Debounce 在收到最后一个事件后等待 t,而 Throttling 在 t 时间内定期执行 fn。
  4. 去抖动用于搜索功能,节流用于实时应用程序(不限于这些)。

资源

前端营
洛达什

以上是去抖和节流的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责声明 Sitemap
PHP中文网:公益在线PHP培训,帮助PHP学习者快速成长!