性能优化之函数节流

性能优化之函数节流
最新回答
菊部地区有血

2020-11-30 18:11:06

函数节流是一种将高频调用函数优化为按固定时间间隔执行的技术,核心目的是减少函数执行次数以提高性能,同时确保关键回调不被遗漏。 以下是具体实现方式、应用场景及总结:

一、函数节流的核心原理
  • 定义:将高频触发的函数调用限制为每隔指定时间执行一次,避免因短时间内多次触发导致的性能问题。
  • 与防抖的区别

    防抖:合并时间间隔内的多次调用,仅在触发结束时执行一次。

    节流:固定时间间隔执行一次,无论触发频率多高。

二、两种基础实现方式及缺陷1. 定时器实现
  • 原理:通过setTimeout延迟执行,若定时器已存在则忽略新调用。
  • 代码:function throttleByTimer(cb, wait) { let timer = null; return function() { if (timer) return; timer = setTimeout(() => { cb.apply(this, arguments); timer = null; }, wait); };}
  • 缺陷首次调用无法立即执行,需等待第一个时间间隔结束。
2. 时间戳实现
  • 原理:记录上次执行时间戳,仅当当前时间与上次时间差超过阈值时执行。
  • 代码:function throttleByTimeStamp(cb, wait) { let preTimeStamp = +new Date(); return function() { let nowTimeStamp = +new Date(); if (nowTimeStamp - preTimeStamp < wait) return; cb.apply(this, arguments); preTimeStamp = nowTimeStamp; };}
  • 缺陷无法确保最后一次调用被执行(若触发停止时未达到时间间隔)。

图:定时器(左)与时间戳(右)实现逻辑对比三、优化后的综合实现

结合两种方式的优点,确保首次立即执行最后一次必定执行

function throttle(cb, wait) { let preTimeStamp = 0; let timer = null; return function() { let nowTimeStamp = +new Date(); let difTimeStamp = nowTimeStamp - preTimeStamp; if (difTimeStamp < wait) { if (timer) return; timer = setTimeout(() => { cb.apply(this, arguments); timer = null; preTimeStamp = +new Date(); }, wait - difTimeStamp); return; } clearTimeout(timer); timer = null; cb.apply(this, arguments); preTimeStamp = nowTimeStamp; };}
  • 关键逻辑

    若时间差不足阈值,设置剩余时间的定时器(确保最后一次执行)。

    若时间差超过阈值,立即执行并更新时间戳。

四、应用场景
  1. 高频事件监听

    mousemove:跟踪鼠标移动时减少计算量。

    scroll:滚动加载数据时避免频繁请求。

  2. 动画与游戏

    控制渲染帧率,避免画面卡顿。

  3. 资源密集型操作

    搜索框实时联想(结合防抖更优)。

    窗口大小调整时的布局重计算。

五、总结与类比
  • 类比:如同水龙头从连续滴水改为定时滴水,既节约资源(减少执行次数)又保证需求(关键回调不丢失)。
  • 实现选择

    定时器实现:适合需确保最后一次执行的场景(如滚动加载到底部)。

    时间戳实现:适合需立即响应的场景(如游戏控制)。

    综合实现:推荐用于大多数业务场景,平衡性能与正确性。

图:函数节流优化过程类比水龙头定时滴水六、使用示例// 监听鼠标移动事件,每1秒执行一次timerPDom.onmousemove = throttle(move, 1000);

通过合理使用函数节流,可显著提升页面性能,尤其在移动端和低性能设备上效果更为明显。实际应用中需根据场景选择实现方式,复杂需求可进一步扩展(如添加立即执行参数)。