节流与防抖

这篇具有很好参考价值的文章主要介绍了节流与防抖。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文可以配合本人录制的视频一起食用

作用

节流和防抖是前端开发中常用的优化技术,主要用于优化一些高频触发的事件。

字面理解

节流与防抖,先从字面上理解一下,节流就是节制流入或流出,在前端方面我个人理解一下,指的是节制功能或请求的触发次数,所以节流函数字面上的意思就是防止功能或请求被频繁触发的函数;防抖呢,更好理解,防止抖动,它的字面意思更贴近前端的需求,就是防止页面抖动,以达到更好的用户体验。

适用场景

从字面上的理解可以联想到分别适合这两个功能的场景

先看节流,比如我们打开搜索引擎页面,百度或者Google,当我们在搜索框输入内容,会出现自动补全的下拉框,下拉框里的数据是请求接口获取的,如果不加以限制,就会在频繁输入的时候发送出大量请求,所以节流就可以应用在这类场景中。

再看防抖,当我们在快速上下滚动页面的过程中,如果页面滚动行为绑定了事件监听器,就可能频繁触发回调导致大量的计算从而引发页面的抖动甚至卡顿,防抖函数就可以应用在这类场景中。

所以总体来说,节流和防抖都是用于控制事件触发的频率,只是控制的点不同

防抖更适合于反馈较快的场景,就是说用户操作之后很快就会有反馈,我们不希望反馈太快,并且不希望频繁操作导致要去处理太多的反馈(合并处理);而节流更适合耗时较久的场景,就仿佛某个人在说省点流量吧,我不是没反馈,只是需要多点时间来处理,不要频繁给我发送相同的操作指令。

实现

根据以上理解,我们可以分别来实现这两个函数。

节流

首先是节流。

节流是在某次事件触发时执行指定操作后,再次触发事件时,若两次事件的触发时间点的间隔不小于给定的时间间隔,就再次执行指定操作,否则就不执行。

function throttle(fn, interval) {
  // fn是待执行的操作,interval是给定的时长,在给定的时长内只发送一次操作指令,也就是说只执行一次fn
  // 设置一个变量用于记录
  let last = 0; // 记录上次动作的执行时间
  return function() {
    // 首先保留调用时的this上下文和传入的参数
    let context = this;
    let args = arguments;
    // 记录当前事件触发的时间点
    let now = Date.now();
    // 检查当前时间点与上次执行操作的时间点之间的间隔
    if (now - last >= interval) {
      // 如果当前时间与上次触发动作的时间间隔大于或等于interval
      // 就触发操作
      fn.apply(context, args);
      // 并且更新last为当前时间
      last = now;
    }
    // 否则就不做任何操作,即两次事件触发的时间间隔小于interval时,就不触发fn执行,保证在interval设置的时长内只执行一次fn
  }
}

我们可以在页面上测试一下

<button id="requestButton">
  点我请求
</button>
<script>
// 用throttle包装click的回调,防止频繁请求
const better_request = throttle(() => {
  console.log(Date.now());
}, 3000);
document.querySelector('#requestButton').addEventListener('click', better_request);
</script>

防抖

然后是防抖。

防抖就是在频繁触发事件后,等不再触发事件时合并执行动作。

function debounce(fn, delay) {
  // fn是待执行的操作,delay是指延迟的时长,我们希望在给定的延时之后再执行fn
  // 在防抖函数中需要设置一个定时器,用于延迟执行fn
  let timer = null;
  
  return function() {
    // 保留调用时的this上下文和传入的参数
    let context = this;
    let args = arguments;
    
    // 每次事件被触发时,都去清除之前的旧定时器
    if (timer) clearTimeout(timer);
    // 设定新定时器
    // 在给定的delay延时之后,fn才会被执行
    // 当事件首次被触发,fn会在delay毫秒后执行
    timer = setTimeout(() => {
      fn.apply(context, args);
    }, delay);
  }
}
  1. 使用防抖后,在事件触发时,fn在delay毫秒的延迟后才会执行,可以保证回调反馈不会太快
  2. 如果在delay毫秒内,比如第x毫秒时第二次事件回调被触发,此时前一个fn还未被执行,若不清理计时,第二个fn操作会在delay毫秒后被执行,这样就会导致delay毫秒内有两个fn会被触发;
    1. 第一个fn在delay-x毫秒后执行
    2. 第二个fn在delay毫秒后执行
    3. 两个fn的执行间隔理论上为x毫秒,x小于delay
  3. 所以为了保证fn不被频繁执行,我们要将前一个计时清理掉,使得delay延时内只有一个fn将被执行,相当于将多个反馈合并处理
  4. 如果delay延时内再无事件触发,则延时结束后fn就被执行

这样做看上去似乎没有问题,但实际上是存在问题的,问题就在于如果用户操作过于频繁,就会导致fn的执行被无限推迟,因为新的事件触发总会清除掉上一次的计时器,这样用户的操作需要很久才得到反馈,或者根本得不到反馈,比如用户在频繁滚动页面后,没等到fn执行就跳转其他页面了。

合并版

为了保证在给定的时间内必须执行一次fn,我们可以使用throttle来优化防抖,也可以说是两者的合并。

最终要达到的目标:

  1. 将多次事件触发的fn操作合并执行
  2. 在给定的时间间隔内一定会执行一次fn
function enhanceThrottle(fn, delay) {
  // 设置两个变量
  // last用于记录上一次fn执行的时间
  // timer用于延迟执行fn
  let last = 0, timer = null;
  
  return function() {
    // 保留回调时的this上下文和传入的参数
    let context = this;
    let args = arguments;
    // 记录当前事件触发的时间点
    let now = Date.now();
    if (now - last < delay) {
      // 如果当前时间点与上一次fn执行时间的间隔小于给定的时间间隔
      // 不执行fn操作
      // 重置定时器,在delay延时后执行fn
      // 这样执行两次fn预计的时间差就是now - last + delay,也就是说时间差会大于delay
      clearTimeout(timer);
      timer = setTimeout(() => {
        last = Date.now();
        fn.apply(context, args);
      }, delay);
    } else {
      // 当前时间点与上一次fn执行时间的间隔超出给定的时间间隔
      // 就立即执行一次fn
      fn.apply(context, args);
      // 并更新last的值
      last = now;
    }
  }
}

优化之后,在第一次触发事件时,就会立即执行一次fn。

但是这样优化之后依旧存在问题:

就是在else语句这个分支,当前时间点与上一次fn执行时间的间隔超出给定的时间间隔,就立即执行一次fn,假设此次事件的触发时间点是now2,上一次事件的触发时间点是now1,如果经过now1-last+delay这个延迟之后刚好是now2,就会在立即执行fn的同时,有个延迟的fn也要执行。

可以继续优化,在立即执行fn这个分支里,也去重置计时,clearTimeout(timer),当然实践中可能还是会有问题,比如在清理计时器之前这个延迟的fn操作已经进入任务队列了。

对比

两个初始版的节流和防抖。

看上去,节流函数就像在一段时间间隔的开始时间点执行操作,防抖函数像是在一段时间间隔内最后一次事件触发后执行操作。两者似乎是一个头一个尾,但其实上并没有很相似,节流的时间间隔是给定的,而防抖的时间间隔是不确定的,而是视用户的操作而定。

也就是说节流直接丢掉后面的操作,防抖更类似于合并了前面的操作文章来源地址https://www.toymoban.com/news/detail-625853.html

到了这里,关于节流与防抖的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 前端优化 ----防抖 节流

    如果一个事件在短时间内连续触发,则只去执行最后一次。 控制频率 实现方式:每次触发事件时设置一个延迟调用方法,并且取消之前的延时调用方法 缺点:如果事件在规定的时间间隔内被不断的触发,则调用方法会被不断的延迟 使一个函数在固定时间内只执行一次。控制

    2024年02月04日
    浏览(39)
  • 前端刷题-防抖节流

    在实际的开发过程中,一般会使用lodash自有的debounce函数和throttle函数对所要防抖/节流的函数进行包裹。例如

    2024年02月10日
    浏览(42)
  • 节流&防抖

    节流:wait时间内只能执行一次func 防抖:触发后延迟wait秒调用func 代码对比:(建议自己手敲一遍,不要养成直接cv的坏习惯) 节流: 其中的apply函数是    apply() 是 JavaScript 函数的一个方法,它用于调用函数并设置函数内部 this 的指向。 在 throttle() 函数中, func 是传

    2024年02月07日
    浏览(33)
  • 防抖,节流

    防抖(debounce):类似法师技能读条,读条没完再按技能就会重新读条,在触发后的n秒内只会执行一次,若在这n秒内重复触发则重新计算 节流(throttle):连续发生的事件在n秒内只执行一次函数 【前端面试必问】—常见的机试题—瀑布流_哔哩哔哩_bilibili 防抖和节流(详解) 使用

    2024年02月12日
    浏览(41)
  • 防抖和节流有什么区别?

    一、理解防抖和节流 防抖: 在单位时间内频繁触发事件,只有最后一次生效 比如:在游戏回城的时候被打断,再次点回城就会重新计时,最终只有没被打断的最后一次,才能成功回城,就是防抖 节流: 在单位时间内频繁触发事件,只生效一次(也就是只有第一次生效) 比

    2024年02月04日
    浏览(36)
  • 【JS进阶】防抖与节流

    1.防抖 1.1 为什么要防抖? 在项目中,有的操作是 高频触发 的,但是其实触发一次就好了,比如我们短时间内多次缩放页面,那么我们不应该每次缩放都去执行操作,应该只做一次就好。再比如说监听输入框的输入,不应该每次都去触发监听,应该是用户完成一段输入后再进

    2024年02月09日
    浏览(41)
  • vue 防抖与节流用法

    一、html 二、JS 三、公共方法 common.js

    2024年02月10日
    浏览(33)
  • js的防抖与节流

    在 JavaScript 中,大量 操作 都会触发 事件 ,这些 事件 又会被添加到 事件队列 中进行 排队处理 某些事件如果 频繁触发的话会对浏览器的性能造成损耗 ,我们就可以使用 防抖 或者 节流 操作来 限制 事件的执行 频率 防抖 即当一个事件被触发时 不会立即执行 ,而是会 等待

    2024年01月18日
    浏览(39)
  • 【前端】防抖和节流原理+实现

    防抖(Debounce)和节流(Throttle)都是用于控制函数执行频率的机制,它们在处理高频触发的事件时非常有用。 防抖的原理是在事件被触发后,等待一段时间(例如200毫秒)来执行函数,如果在等待时间内事件被再次触发,则重新计时。这样可以避免在短时间内多次触发事件

    2024年04月16日
    浏览(36)
  • 防抖和节流及多种实现方式

    当用户在网页中进行操作时,如点击、滚动、输入等,往往会频繁地触发事件。如果每个事件都立即执行相应的函数,可能会导致性能问题和用户体验不佳,因为这些函数可能需要执行复杂的操作,如计算、网络请求等。 为了优化这种情况,我们可以使用防抖和节流来限制函

    2023年04月24日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包