“重读”防抖和节流

247 阅读10分钟

小编认为的比较有意义的延伸面试点考察

为什么使用节流的方式去处理消息发送事件

使用节流的方式来处理消息发送和按钮提交时间,主要是为了在一定时间内限制执行频率,防止短时间内的重复触发。虽然防抖也可以控制频率,但节流在这些情况下更适合。

  • 防止频繁提交、发送,且允许固定时间间隔内执行。
    • 在消息发动或按钮提交的场景中,通常希望短时间内只允许用户点击一次,例如每隔1s或2s。节流可以确保在设定的间隔内执行一次,而防抖在这个场景下会导致执行时间不确定。
    • 使用节流可以让用户在每个固定间隔后再次触发事件,例如每秒发送一次消息,用户不需要等待太长的延迟。防抖则需要用户停止点击,等待延迟时间到达后才会触发,这对用户体验会有影响。
  • 节流避免延迟执行带来的响应不及时
    • 防抖在用户频繁操作时会不断重置计时器,只有在用户完全停止操作的情况下才会触发一次。而消息发送和表单提交通常希望在每次点击时都执行操作,只是限制频率而已。
    • 例如:在聊天场景中,如果用户多次点击“发送”按钮,使用防抖会让消息发送变得不确定,用户可能需要等待操作完成,影响即时性。使用节流则可以确保在设定的间隔内最多触发一次发送,而不需要等待额外的延迟。
  • 节流适合限制操作频率而不影响正常使用
    • 在用户需要连续操作但又希望限制操作频率的情况下,节流更合适,因为它允许用户每隔一段时间就可以触发一次。而防抖则会导致在频繁操作时没有任何触发结果。
    • 按钮提交和消息发送场景中,用户通常会希望即时收到响应(如发送成功的反馈),而不是等待延迟。节流在频率控制的同时不会打断操作的连续性,符合这类场景的需求。
小总结

在消息发送和按钮提交等场景中,节流可以通过固定时间间隔触发,防止短时间内多次触发,同时让用户体验到即时响应。而防抖在这里则容易导致延迟和响应不确定,不适合这些场景。所以,节流更适合在需要限制频率但不影响响应即时性的场景中使用。

在两者互相嵌套的情况下,会有什么问题

debounce包在throttle内部

这种组合方式可以确保函数在固定时间间隔内触发,但在用户停止操作时不会再额外的触发调用。具体来说,throttle会限制频率,而debouncethrottle触发时进行最后一次延迟调用。

  • 执行效果

    const throttledDebouncedFunc = _.throttle(_.debounce(func, 1000), 5000);
    
    • 外层_.throttle:确保func最多每5000ms触发一次
    • 内部_.debounce:确保在1000ms毫秒无操作时才调用func,即每次throttle触发时,debounce会先延迟执行。
  • 这样组合效果是:

    • 每 5000ms 内最多触发一次,并且这次触发还需要满足 debounce 的延迟条件。

    • 即使在频繁操作时,func 也会在指定的 throttle间隔后触发。

    • 当用户停止操作 1000ms 后,func 会通过 debounce 的延迟被调用。

  • 适用场景:适合需要频率控制但希望一定延迟后再执行的场景。

    • 延迟加载内容: 滚动条快速滚动时,不想频繁请求加载数据,但希望在每次间隔结束后等待一段时间来确认操作是否停止。

    • 避免频繁操作触发敏感操作: 比如间隔时间内有大量操作时,throttle控制频率,而debounce则确保触发事件有延迟,避免过早执行操作。

throttle包在debounce内部

这种方式呢,会在频繁调用之后全部被防抖掉。这里会分为两种情况:

  • 如果节流的延迟时间小于防抖时间,throttle 将不会再起作用,所以小编认为这种方式没有意义。

  • 如果节流的延迟时间大于防抖时间,throttle 将会频率限制保存。比如1s的防抖你可以触发三次保存,但是3s的节流可以限制你保存1次。

小总结
  • debouncethrottle内部:确保在触发频率内有延迟,且在频率外等待操作停止后执行。适合延迟执行的节流场景。
  • throttledebounce内部:用于高频事件中,在频繁操作后保证最终触发,适合在操作结束时执行一次的场景。而这种方式呢,可以只使用 debounce

防抖(Debounce)与节流(Throttle)的定义

  • 防抖(Debounce): 一种在事件被频繁触发的情况下,确保事件处理函数只会在最后一次触发后的一段延迟时间才执行。
    • 例子:用户输入搜索框时不断触发搜索请求,通过防抖可以请求只在用户停止输入后执行。
  • 节流(Throttle): 一种在事件被频繁触发的情况下,确保事件处理函数在设定的时间间隔内最多只会执行一次。
    • 例子:监听滚动事件时可以使用节流,避免页面频繁计算或重新渲染。

使用场景分析

  • 防抖的常见使用场景:
    • 搜索框输入:减少请求次数,避免频繁请求接口。
    • 调整窗口大小:页面元素布局调整只在窗口调整完毕后触发。
    • 表单校验:实时校验表单输入内容,减少计算频率。
  • 节流的常见使用场景:
    • 滚动加载:避免滚动事件频繁触发请求。
    • 页面元素拖拽:控制拖拽事件触发频率,提高性能。
    • 按钮点击:防止用户频繁点击触发多次操作。

防抖(Debounce)实现方式

  • 实现思路: 每次事件触发时,清除前一个计时器,重新设置计时器。

  • 代码实现

    function debounce(func, delay) {
        let timer = "";
        return function(...args) {
            // 清除前一个定时器
            clearTimeout(timer);
            // 设置新的定时器
            timer = setTimeout(() => {
                func.apply(this, args);
            }, delay)
        }
    }
    
  • 详解:

    • timer:用于存储当前的计时器。
    • clearTimeout:每次时间触发时清除上一次的计时器,确保只在最后一次触发后执行。
    • setTimeout:在设定的延迟时间后执行传入的函数。

节流(Throttle)实现方式

  • 实现思路: 记录上次触发事件的时间,与当前时间比较,如果时间差大于设定的间隔则执行。

  • 代码实现:

    function throttle(func, delay) {
        let lastTime = 0;
        return function(...args) {
            const now = new Date().getTime();
            if(now - lastTime >= delay){
                lastTime = now;
                func.apply(this, args);
            }
        }
    }
    
  • 详解:

    • lastTime:记录上一次执行的时间。
    • Date.now():获取当前时间,计算时间差,判断是否满足时间间隔。
    • func.apply:条件满足时,执行传入的函数。

市面使用量居多的 lodash 中的防抖和节流方法

  • Lodash: Lodash是一个JavaScript工具库,其中提供了_.debounce_.throttle方法,适用于防抖和节流。
  • 使用方式:
    • _.debounce(func, wait, [options])
    • _.throttle(func, wait, [options])

尤其注意,options 中的参数可能会导致输出结构会与想象中有些差别,比如节流的参数可能会导致最终执行完成之后再次触发。

防抖和节流的优缺点

  • 防抖的优缺点
    • 优点:确保只执行一次,避免频繁操作。
    • 缺点:用户可能需要等待延迟结束才可以看到反馈,不适用于需要即时反馈的情况。
  • 节流的优缺点:
    • 优点:通过控制频率降低开销,适合高频率触发的事件。
    • 缺点:不够精准,可能导致用户在等待间隔期间无法得到即时反馈。

综合应用场景

  • 表单自动保存功能

    • 案例描述

      在表单中进行输入时,为了避免用户每次输入都触发保存操作,使用节流和防抖组合的方式来控制频率。在用户输入时,每隔一段时间保存一次,输入停止后延迟一段时间再做一次最终保存

    • 代码示例

      const saveData = (data) => {
          console.log("自动保存数据:", data);
      }
      
      // 先使用节流,确保每5s最多触发一次
      const throttledSave = _.throttle(saveData, 5000);
      
      // 然后使用防抖,确保用户停止输入1s后触发最终保存
      const saveDataWithDebAndThr = _.debounce(throttledSave, 1000)
      
      // 监听输入框的输入事件
      document.querySelector('#input').addEventListener('input', (event) => {
          const {value} = event.target;
          saveDataWithDebAndThr(value)
      })
      
  • 滚动事件监听与加载新的内容

    • 案例描述

      在长页面中使用无限滚动加载内容时,可以通过节流限制滚动事件触发频率,避免频繁请求新内容。同时在用户停止滚动时使用防抖,确保滚动结束后再次触发检查加载。

    • 代码示例

      const loadMore = () => {
          console.log("加载更多内容...")
      }
      
      // 滚动过程中的节流,限制每500ms检查一次
      const throttled = _.throttle(loadMore, 500);
      // 滚动停止之后的防抖,确保停止300ms后再次检查
      const throttled = _.debounce(loadMore, 300);
      // 监听滚动事件
      window.addEventListener('scroll', () => {
          throttled();
          throttled();
      });
      
  • 表单字段实时校验与提交按钮控制

    • 案例描述

      在表单对每个字段进行实时校验,通过防抖来控制检验频率,避免每次输入都触发校验逻辑。同时对提交按钮进行节流,确保短时间内不会多次触发提交。

    • 代码示例

      const validData = (input) => {
          console.log('校验输入:', input);
      }
      
      // 防抖校验输入字段, 用户停止输入 300ms 后触发
      const debouncedValid = _.debounce(validData, 300);
      
      const submitForm = () => {
          console.log("提交表单");
      }
      
      // 提交按钮的节流,限制每隔2s最多提交一次
      const throttledSubmit = _.throttle(submitForm, 2000);
      // 监听输入框的输入事件
      document.querySelector('#inputField').addEventListener('input', (event) => {
          const {value} = event.target;
          debouncedValid(value)
      })
      // 监听提交按钮点击事件
      document.querySelector("#submitBtn").addEventListener('click', submitForm)
      
  • 窗口调整时的图表重绘

    • 案例描述

      在窗口调整大小过程中,可以通过节流来控制图表重绘频率,防止频繁触发消耗性能。同时在调整结束之后使用防抖进行最终一次的精准重绘。

    • 代码示例

      const resizeChart = () => {
          console.log("重绘图表");
      }
      
      // 窗口调整时的节流,每 500ms 重绘一次图表
      const throttledResize = _.throttle(resizeChart, 500)
      
      // 窗口调整结束后的防抖,确保调整停止 200 ms 后进行最终重绘
      const debouncedResize = _.debounce(resizeChart, 200)
      
      // 监听窗口调整事件 
      window.addEventListener('resize', () => {
          throttledResize()
          debouncedResize()
      })
      
  • 实时聊天中的消息发送按钮

    • 案例描述

      在实时聊天场景中,避免用户快速点击发送按钮导致重复消息。使用节流来限制点击的频率,每隔一段时间才能发送一条消息。

    • 代码示例

      const sendMessage = () => {
      	console.log("发送消息");
      }
      
      // 使用节流限制每1s最多发送一次消息
      const throttledSend = _.throttle(sendMessage, 1000);
      
      // 监听发送消息按钮点击事件
      document.querySelector('#sendButton').addEventListener('click', throttledSend)