鸿蒙应用开发-性能优化:防抖debounce和节流throttle

  1. 在开发中我们经常会遇到事件频繁回调导致的性能和体验下降的问题,比如滚动事件回调或者输入框根据用户输入实时返回sug的场景。
  2. 一方面用户并不需要那么精确的实时,另一方面实时的操作会发送不必要的多余请求,给服务端造成压力,也给端上造成困扰。

throttle和debounce就是为了解决这类问题出现的

节流 throttle

限制函数的执行频率,保证一定时间内只执行一次函数调用。无论触发频率多高,都会在指定时间间隔内执行一次函数。

举个例子

假设我们在排队进电影院,进入电影院的规则是 每10分钟进一波人

第一个人2:00来了,他需要2:10才能进,第二个人2:02来了,他也需要2:10才能进,第三个人2:09来了,他也需要2:10才能进。第四个人2:11来了,他需要在2:20才能进

场景

用于处理连续触发的事件,比如滚动事件、鼠标移动事件等。控制函数的执行频率,以减少资源消耗和提高性能。

实现示例

js 复制代码
// delay表示延迟的时间间隔(单位毫秒),callback是需要进行防抖的函数
function throttle(delay, callback) {
  let timeoutID;
  let lastExec = 0;

  function wrapper() {
    // 🌟🌟🌟通过 `self` 变量临时保存 `this` 的值,从而在 `exec` 函数中通过 `callback.apply(self, args)` 传入正确的 `this` 值
    const self = this;
    // 计算距离最近一次函数执行后经过的时间 `elapsed`
    const elapsed = Number(new Date()) - lastExec;
    const args = arguments;

    function exec() {
      // 更新最近一次函数的执行时间
      lastExec = Number(new Date());
      callback.apply(self, args);
    }
    // 并清除之前设置的计时器
    clearTimeout(timeoutID);

    // 如果经过的时间大于设置的时间间隔 `delay`,那么立即执行函数
    if (elapsed > delay) {
      exec();
    } else {
      // 如果经过的时间小于设置的时间间隔 `delay`,那么通过 `setTimeout` 设置一个计数器,让函数在 `delay - elapsed` 时间后执行
      timeoutID = setTimeout(exec, delay - elapsed);
    }
  }

  return wrapper;
}

使用示例

js 复制代码
// 实际做事的函数
function foo() { console.log('foo..'); }
// 对foo函数进行节流,返回新的函数
const fooWrapper = throttle(200, foo);
for (let i = 1; i < 10; i++) { 
    // 使用节流函数,限制每200ms执行一次
    setTimeout(fooWrapper, i * 30); 
}
// => foo 执行了三次 
// => foo.. 
// => foo.. 
// => foo..

这两句代码是非常有必要的 const self = this; const args = arguments;

我们知道在JS非箭头函数中的this并不是定义时的上下文,而是执行时的上下文,而我们的debounce把原来的函数做了一层包装会导致执行时this的改变。这里的两行代码就巧妙的解决了这个问题

防抖 debounce

在事件被触发n秒后去执行回调函数。如果n秒内该事件被重新触发则重新计时。结果就是将频繁触发的事件合并为一次,且在最后执行

举个例子

假设我们在排队进电影院,进入电影院的规则是 最后一个人来了之后等10分钟没有人再来才可以进去,否则大家一起等。

第一个人来了,他需要等10分钟看有没有其他人来,如果10分钟内第二个人来了,那么第一个人和第二个人需要重新开始等10分钟看有没有其他人来,同样的道理,如果10分钟内第三个人来了,那么第一个人、第二个人、第三个人需要重新开始等10分钟看有没有其他人来,依次类推。假设第5个人来了,这五个人等了10分钟后没有人再来,他们就可以进影院了

场景

用于处理频繁触发的事件,比如窗口大小调整、搜索框输入等。避免函数执行过多次,以减少网络开销和性能负担

注意事项

我们从debounce的原理可以知道,如果时间间隔很长事件可能永远不会触发。所以我们使用的时候这个时间设置的不宜太长

实现示例

js 复制代码
// `debounce` 函数可以借助 `throttle` 函数实现
function debounce(delay, callback) {
  // 保存超时id
  let timeoutID;

  function wrapper() {
     // 🌟🌟🌟通过 `self` 变量临时保存 `this` 的值,从而在 `exec` 函数中通过 `callback.apply(self, args)` 传入正确的 `this` 值
    const self = this;
    const args = arguments;

    function exec() {
      callback.apply(self, args);
    }
    
    // 每次清空定时器,并重新设置定时器
    // 这里就做到了在规定时间内多次函数调用把前一个取消的效果
    clearTimeout(timeoutID);

    timeoutID = setTimeout(exec, delay);
  }

  return wrapper;
}

使用示例

js 复制代码
function bar() { console.log('bar..'); } 
const barWrapper = debounce(200, bar); 
for (let i = 1; i < 10; i++) { 
    setTimeout(barWrapper, i * 30); 
} 
// => bar 执行了一次
// => bar..

参考资料

  1. JavaScript 高级系列之节流 [throttle] 与防抖 [debounce]
  2. 防抖(Debounce) & 节流(Throttle)
  3. 每天阅读一个 npm 模块(4)- throttle-debounce
  4. 前端性能优化的利器-JS中的防抖和节流
  5. 说说 JavaScript 中函数的防抖 (Debounce) 与节流 (Throttle)
  6. 浅谈Debounce 与 Throttle
  7. [JS性能优化]函数去抖(debounce)与函数节流(throttle)
  8. lodash中的throttle
  9. lodash中的debounce
  10. throttle-debounce库
相关推荐
大雷神1 小时前
HarmonyOS APP<玩转React>开源教程二十一:测验服务层实现
前端·react.js·开源·harmonyos
qq_283720051 小时前
Qt QML 中为 ComBox设置鸿蒙字体(HarmonyOS Sans)——适配 Qt 5.6.x 与 Qt 5.12+
c++·qt·harmonyos
花先锋队长2 小时前
华为音乐世界睡眠日特别策划上线,在沉浸式空间音频摇篮曲中入梦
华为·智能手机·harmonyos
卡兰芙的微笑2 小时前
对鸿蒙蓝牙接口进行xts用例编写
华为·harmonyos
ShuiShenHuoLe2 小时前
管理数据的状态
harmonyos·鸿蒙
大雷神2 小时前
HarmonyOS APP<玩转React>开源教程二十二:每日一题功能
前端·react.js·开源·harmonyos
●VON3 小时前
【复盘】鸿蒙实战营 Day 1:环境搭建遇冷,我们该如何破局?
华为·harmonyos·von
前端不太难3 小时前
做了一个 AI 鸿蒙 App,我发现逻辑变了
人工智能·状态模式·harmonyos
枫叶丹43 小时前
【HarmonyOS 6.0】Network Kit 深度解析:TLS 认证全面支持国密证书
开发语言·网络安全·华为·harmonyos
前端不太难5 小时前
AI 原生架构:鸿蒙App的下一代形态
人工智能·架构·harmonyos