防抖

防抖 debounce

场景

  • 搜索提示
    • 如果默认情况下,输入一个字符就触发送一次请求,这样太浪费性能。
    • 加入防抖后,指定一个最大触发时间,用户在这个时间内输入内容后,就会重新开始计时,只有超过该时间间隔才会发送请求
    • 优势:避免频繁触发事件,导致的性能问题
  • 其他场景: window 的 resize / scroll 事件等

生活中的例子:乘坐电梯,假设你正在电梯里面 电梯门开始关闭,突然,另一个人进入电梯。这个时候,电梯门打开了,它没有升降。 现在又有一个人要进入电梯,又执行了上述相同的功能。 电梯延迟了它的功能(升降楼层),但是,优化了电梯的利用率。王者荣耀的回城也是一样的,只有在最后一次点击回城时才开始回城。

实现过程1

  • 基本实现:
const debounce = (func, wait) => {
    let timerId
    return () => {
        clearTimeout(timerId)
        timerId = setTimeout(() => {
            func()
        }, wait)
    }
}
// 测试:

let count = 0
const getCity = () => console.log('鼠标移动了', ++count)
// 鼠标移动事件,在鼠标停止移动后,再经过 500ms 才会执行
window.onmousemove = debounce(getCity, 500)

实现过程2

  • 处理 this 指向:
const debounce = (func, wait) => {
  let timerId

  // 注意:此处不要使用箭头函数,箭头函数 this 指向的问题!!!
  return function() {
    const context = this

    clearTimeout(timerId)
    timerId = setTimeout(() => {
      func.apply(context)
    }, wait)
  }
}
// 测试

<div id="container" style="height: 100px; background-color: skyblue"></div>

// 注意:箭头函数中 this 指向问题
function getCity() {
    console.log('鼠标移动了 this:', this)
}
const container = document.getElementById('container')
// 鼠标移动事件,在鼠标停止移动后,再经过 500ms 才会执行
container.addEventListener('mousemove', debounce(getCity, 500))

实现过程3

  • 事件对象(处理参数):
const debounce = (func, wait) => {
  let timerId

  return function() {
    const context = this
    const args = arguments

    clearTimeout(timerId)
    timerId = setTimeout(() => {
      func.apply(context, args)
    }, wait)
  }
}

// const fn = debounce((arg) => {}, 500)
// fn(123)
// 测试:

function getCity(e) {
  console.log('鼠标移动了,事件对象:', e)
}
const container = document.getElementById('container')
container.addEventListener('mousemove', debounce(getCity, 500))

实现过程4

  • 立即执行:
const debounce = (func, wait, leading) => {
  let timerId

  return function() {
    const context = this
    const args = arguments

    if (timerId) clearTimeout(timerId)
    if (leading === true) {
      if (!timerId) func.apply(context, args)

      timerId = setTimeout(() => {
        // 重置 timerId 的值。
        timerId = null
      }, wait)
    } else {
      timerId = setTimeout(() => {
        func.apply(context, args)
      }, wait)
    }
  }
}
// 测试:

container.addEventListener('mousemove', debounce(getCity, 500, true))
  • 分解步骤1:
// 1 添加立即执行判断
const debounce = (func, wait, leading) => {
  let timerId

  return function() {
    const context = this
    const args = arguments

    if (leading === true) {
      clearTimeout(timerId)
      timerId = setTimeout(() => {
        func.apply(context, args)
      }, wait)
    }
  }
}
  • 分解步骤2:
// 2 立即调用
const debounce = (func, wait, leading) => {
  let timerId

  return function() {
    const context = this
    const args = arguments

    if (leading === true) {
      // 问题:因为直接调用了 func,没有了 debounce 的特性
      func.apply(context, args)

      clearTimeout(timerId)
      timerId = setTimeout(() => { }, wait)
    }
  }
}
  • 分解步骤3:
// 3 调用一次
const debounce = (func, wait, leading) => {
  let timerId
  let callNow = true

  return function() {
    const context = this
    const args = arguments

    if (leading === true) {
      // 问题:只会触发一次
      if (callNow) func.apply(context, args)
      callNow = false

      clearTimeout(timerId)
      timerId = setTimeout(() => { }, wait)
    }
  }
}
  • 分解步骤4:
// 4 实现debounce效果
const debounce = (func, wait, leading) => {
  let timerId
  let flag = true

  return function() {
    const context = this
    const args = arguments

    if (leading === true) {
      if (flag) func.apply(context, args)
      flag = false

      clearTimeout(timerId)
      timerId = setTimeout(() => {
        // 在间隔时间达到后,重置标志。这样,再触发该事件时,上面的 if(flag) 判断就成了
        flag = true
      }, wait)
    }
  }
}
  • 分解步骤5:
// 5 使用 timerId 代替 flag
const debounce = (func, wait, leading) => {
  let timerId

  return function() {
    const context = this
    const args = arguments

    if (leading === true) {
      if (!timerId) func.apply(context, args)

      clearTimeout(timerId)
      timerId = setTimeout(() => {
        // 重置 timerId 的值。
        timerId = null
      }, wait)
    }
  }
}

实现过程5

  • 返回值:只能处理 leading=true 的情况
const debounce = (func, wait, leading) => {
  let timerId, result

  return function() {
    const context = this
    const args = arguments

    if (timerId) clearTimeout(timerId)
    if (leading === true) {
      if (!timerId) result = func.apply(context, args)

      timerId = setTimeout(() => {
        // 重置 timerId 的值。
        timerId = null
      }, wait)
    } else {
      timerId = setTimeout(() => {
        func.apply(context, args)
      }, wait)
    }

    return result
  }
}
// 测试

function getCity(e) {
    console.log('鼠标移动了,事件对象:', e)
    return 666
}
const container = document.getElementById('container')
const btnCancel = document.getElementById('container')
const handleMouseMove = debounce(getCity, 2000, true)
container.addEventListener('mousemove', handleMouseMove)

// 结果: 666
console.log('测试返回值:', handleMouseMove())

实现过程6

  • 取消
const debounce = (func, wait, leading) => {
  let timerId, result

  function debounced() {
    const context = this
    const args = arguments

    if (timerId) clearTimeout(timerId)
    if (leading === true) {
      if (!timerId) result = func.apply(context, args)

      timerId = setTimeout(() => {
        // 重置 timerId 的值。
        timerId = null
      }, wait)
    } else {
      timerId = setTimeout(() => {
        func.apply(context, args)
      }, wait)
    }
    return result
  }

  // 取消
  debounced.cancel = function() {
    clearTimeout(timerId)
    timerId = null
  }

  return debounced
}
// 测试:

function getCity(e) {
  console.log('鼠标移动了,事件对象:', e)
}
const container = document.getElementById('container')
const btnCancel = document.getElementById('container')
const handleMouseMove = debounce(getCity, 2000, true)
container.addEventListener('mousemove', handleMouseMove)

btnCancel.addEventListener('click', () => {
  handleMouseMove.cancel()
})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
防抖(Debouncing)是一种在处理用户输入事件时常用的优化技术,目的是减少频繁且连续发生的请求对系统性能的影响。在Spring Boot应用中,尤其是与前端交互频繁的服务中,防抖可以帮助我们控制服务端对客户端触发的请求的响应频率,避免因用户的快速操作而造成不必要的资源消耗。 在Spring Boot中,虽然没有内置的防抖功能,但你可以利用一些第三方库或者自定义逻辑来实现这个需求。常见的做法是在发送请求之前设置一个定时器(比如使用`java.util.Timer`或`java.time.Duration`),如果在设定的时间内(例如200毫秒、500毫秒)内多次触发了相同的事件,只执行一次请求,并清除后续的请求。这样可以防止短时间内的多次重复操作导致的过度负载。 以下是一个简单的自定义方法实现防抖的例子: ```java import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class DebounceUtil { private ScheduledExecutorService debounceExecutor = Executors.newScheduledThreadPool(1); private final long debounceTimeout; // 防抖时间,单位毫秒 public DebounceUtil(long debounceTimeout) { this.debounceTimeout = debounceTimeout; } public void debounceCallable(Callable<Void> task) { debounceExecutor.schedule(() -> { try { task.call(); // 如果任务成功完成,取消后续的任务 debounceExecutor.shutdownNow(); } catch (Exception e) { // 处理异常并保留后续任务 e.printStackTrace(); } }, debounceTimeout, TimeUnit.MILLISECONDS); } // 使用示例 public void handleUserInput(String userInput) { debounceCallable(() -> handleInput(userInput)); } private void handleInput(String userInput) { // 这里是处理用户输入的实际业务逻辑 } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值