鸿蒙应用开发-性能优化:防抖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库
相关推荐
SuperHeroWu731 分钟前
【HarmonyOS】键盘遮挡输入框UI布局处理
华为·harmonyos·压缩·keyboard·键盘遮挡·抬起
sanzk5 小时前
华为鸿蒙应用开发
华为·harmonyos
SoraLuna9 小时前
「Mac畅玩鸿蒙与硬件28」UI互动应用篇5 - 滑动选择器实现
macos·ui·harmonyos
ClkLog-开源埋点用户分析10 小时前
ClkLog企业版(CDP)预售开启,更有鸿蒙SDK前来助力
华为·开源·开源软件·harmonyos
mg66810 小时前
鸿蒙系统的优势 开发 环境搭建 开发小示例
华为·harmonyos
lqj_本人11 小时前
鸿蒙next选择 Flutter 开发跨平台应用的原因
flutter·华为·harmonyos
lqj_本人11 小时前
使用 Flutter 绘制一个棋盘
harmonyos
lqj_本人14 小时前
Flutter&鸿蒙next 状态管理框架对比分析
flutter·华为·harmonyos
青瓷看世界15 小时前
华为HarmonyOS打造开放、合规的广告生态 - 插屏广告
华为·harmonyos·广告投放
青瓷看世界16 小时前
华为HarmonyOS借助AR引擎帮助应用实现虚拟与现实交互的能力2-管理AR会话
华为·ar·harmonyos·虚拟现实