首页 > 其他分享 >节流与防抖

节流与防抖

时间:2023-08-05 12:22:31浏览次数:40  
标签:触发 防抖 节流 delay 执行 fn

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

作用

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

字面理解

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

适用场景

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

先看节流,比如我们打开搜索引擎页面,百度或者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操作已经进入任务队列了。

对比

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

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

也就是说节流直接丢掉后面的操作,防抖更类似于合并了前面的操作

标签:触发,防抖,节流,delay,执行,fn
From: https://www.cnblogs.com/beckyyyy/p/17607756.html

相关文章

  • 前端性能优化的利器 ——— 浅谈JavaScript中的防抖和节流
    防抖和节流函数是工作中两种常用的前端性能优化函数,今天我就来总结一下什么是防抖和节流,并详细说明一下如何在工作中应用防抖和节流函数什么是防抖和节流?在JavaScript中,防抖(debounce)和节流(throttle)是用来限制函数执行频率的两种常见技术。防抖(debounce)是指在某个时间段内......
  • js实现输入框防抖功能
    <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=......
  • 深入理解TCP作为面向字节流协议的工作原理
    TCP(传输控制协议)是互联网中广泛使用的传输层协议,它负责可靠地传输数据流。一个重要的特性是TCP被称为面向字节流的协议。本文将详细介绍TCP作为面向字节流协议的含义,其工作原理以及与面向消息的协议的区别。1.什么是TCP面向字节流协议?TCP作为面向字节流的协议意味着数据在发送端......
  • 什么是防抖和节流
    防抖和节流是前端开发中常用的两种性能优化技术。防抖:(Debouncing)的含义是指在一定时间内,多次触发同一个事件,只执行最后一次操作。节流(Throttling)的含义是指在一定时间内,多次触发同一个事件,只执行第一次操作。在防抖函数和节流函数中,都使用了闭包来保存定时器变量timer和......
  • 节流函数
    防抖和节流都是对高频事件进行优化,节流是在规定的时间内执行一次操作1window.onscroll=throttle(function(){2alert(1)3},1000)4functionthrottle(fn,delay){5lett=true;6returnfunction(){......
  • 防抖函数
    防抖是防止连续触发事件,只触发最后一次事件,可以使用防抖函数。简单例子1letoinput=document.querySelector('input')2lett=null;3oinput.oninput=function(e){45if(t!==null){6......
  • 91.介绍一下js的节流与防抖
    91.介绍一下js的节流与防抖?相关知识点://函数防抖:在事件被触发n秒后再执行回调,如果在这n秒内事件又被触发,则重新计时。//函数节流:规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。//函......
  • Java IO流 - 字节流的使用详细介绍
    IO流的基本介绍:IO流的概述:i表示intput,是数据从硬盘文件读入到内存的过程,称之输入,负责读。o表示output,是内存程序的数据从内存到写出到硬盘文件的过程,称之输出,负责写。IO流的分类:按方向分类:输入流输出流按流中的数据最小单位分为:按流中的数据最小单位分为:字......
  • 防抖,节流函数
    //防抖函数functiondebounce(func,wait){//创建一个定时器lettimeout//返回一个函数,这个函数会在一个时间区间结束后调用funcreturnfunction(){//如果定时器还在运行,则清除定时器timeout&&clearTimeout(timeout)//否则,设定定时器,等待w......
  • Java IO:字节流、字符流、缓冲流
    原文:https://blog.csdn.net/mu_wind/article/details/108674284流的特性有下面几点:先进先出:最先写入输出流的数据最先被输入流读取到。顺序存取:可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据。(RandomAccessFile除外)只读或只写:......