前端概念 | 节流(Throttle)

629 阅读3分钟

上一篇文章, 我们在阿飞的帮助下, 一起学习了防抖(Debounce)这一概念

既然谈到了防抖, 节流(Throttle)自然也是不能放过的, 在本篇文章中, 我们将一起探讨节流

概念理解

依然在打工的阿飞

继续请出我们的朋友阿飞, 他依然在餐厅打工当服务生. 虽然点单的流程已经进行了防抖处理, 但一天到晚的工作还是让他累得不行

于是他决定: 一周只打五天工

当周一早晨的闹钟响起, 阿飞不得不起床去餐厅, 因为这周连一天都还没有工作; 当周四早晨的闹钟响起, 阿飞依然得离开温暖的被窝, 因为这周还需要工作两天

可当周六早晨的闹钟响起, 阿飞可以把闹钟关掉, 翻个身继续美梦, 因为我们的朋友这周已经工作五天了, 今天是他的休息日

一周工作满五天后休息两天, 劳逸结合, 这样阿飞无论是在工作中还是生活中都能够保持精力充沛

类比

我们在前端程序设计中也可以像阿飞安排工作一样, 把一些函数在单位时间内被执行的次数固定, 从而降低相关资源的负荷, 提升性能表现

一种实现

直接从节流的概念出发, 我们很容易想到: 可以引入一个标记来记录函数在当前单位时间内是否已经被执行过

若未执行过, 则立即执行相应函数, 并将该标记设置为已执行过, 且直到下一个单位时间才刷新标记, 从而控制函数在单位时间被执行的次数

/**
 * 为已有函数设置节流
 *
 * @param {Function} func 将设置节流的函数
 * @param {number} limit 单位时间
 * @return {Function} 设置节流后的函数
 */
const throttle = (func, limit) => {
    // 标记单位时间内是否已经执行对应函数
    let inThrottle;

    return function(...args) {
        // 若标记显示当前单位时间内未执行, 则执行对应函数并设置标记
        if (!inThrottle) {
            const context = this;

            Reflect.apply(func, context, args);
            inThrottle = true;
            setTimeout(() => inThrottle = false, limit);
        }
    }
}

测试

我们可以基于 onscroll 来粗略展示一下节流的效果

  • HTML

    <!DOCTYPE html>
    <html lang="zh-CN">
    
    <head>
        <meta charset="UTF-8">
        <title>throttle</title>
    </head>
    
    <body>
        <div id="demo" style="height: 1000px"></div>
        <script src="./throttle.js"></script>
    </body>
    
    </html>
    
  • JavaScript

    /**
    * 为已有函数设置节流
    *
    * @param {Function} func 将设置节流的函数
    * @param {number} limit 单位时间
    * @return {Function} 设置节流后的函数
    */
    const throttle = (func, limit) => {
        // 标记单位时间内是否已经执行对应函数
        let inThrottle;
    
        return function (...args) {
            // 若标记显示当前单位时间内未执行, 则执行对应函数并设置标记
            if (!inThrottle) {
                const context = this;
    
                Reflect.apply(func, context, args);
                inThrottle = true;
                setTimeout(() => inThrottle = false, limit);
            }
        }
    }
    
    const demo = document.getElementById("demo");
    let COUNT = 0;
    function count() {
        demo.innerHTML += (`上下滑动对应回调函数被执行了${++COUNT}次<br>`);
    }
    
    // 添加节流前
    // window.onscroll = count;
    
    // 添加节流后
    window.onscroll = throttle(count, 3000);
    
  • 添加节流前

    节流-原始.gif

  • 添加节流后

    节流.gif

从上述两张 Gif 图中, 我们可以看出: 在添加节流之前, onscroll 对应的函数将会随着页面的上下滑动被执行多次; 而在添加节流之后, 对应函数在单位时间内只会被执行一次, 直到下一个单位时间才会被再次执行

总结

通过节流, 我们可以主动地控制相关函数的执行频率, 从而避免事件在短时间内大量触发造成页面卡顿

除了在上文中提到的 onscroll, 还有按钮重复点击和 touchmove 等都可以使用节流

以上是我对节流(Throttle)的理解, 如果文章中存在问题或是大家有更好的理解, 欢迎大家指出和交流:)