节流

简介: 【10月更文挑战第17天】

为什么要限制事件的频繁触发,以及如何做限制:

1.debounce 防抖

2.throttle 节流

今天重点讲讲节流的实现。

节流
节流的原理很简单:

如果你持续触发事件,每隔一段时间,只执行一次事件。

根据首次是否执行以及结束后是否执行,效果有所不同,实现的方式也有所不同。

我们用 leading 代表首次是否执行,trailing 代表结束后是否再执行一次。

关于节流的实现,有两种主流的实现方式,一种是使用时间戳,一种是设置定时器。

使用时间戳
让我们来看第一种方法:使用时间戳,当触发事件的时候,我们取出当前的时间戳,然后减去之前的时间戳(最一开始值设为 0 ),如果大于设置的时间周期,就执行函数,然后更新时间戳为当前的时间戳,如果小于,就不执行。

// 第一版

function throttle(func, wait) {
   

    var context, args;

    var previous = 0;

    return function() {
   

        var now = +new Date();

        context = this;

        args = arguments;

        if (now - previous > wait) {
   

            func.apply(context, args);

            previous = now;

        }

    }

}

container.onmousemove = throttle(getUserAction, 1000);

我们可以看到:当鼠标移入的时候,事件立刻执行,每过 1s 会执行一次,如果在 4.2s 停止触发,以后不会再执行事件。

使用定时器
接下来,我们讲讲第二种实现方式,使用定时器。当触发事件的时候,我们设置一个定时器,再触发事件的时候,如果定时器存在,就不执行,直到定时器执行,然后执行函数,清空定时器,这样就可以设置下个定时器。

// 第二版

function throttle(func, wait) {

var timeout;

var previous = 0;

return function() {

    context = this;

    args = arguments;

    if (!timeout) {

        timeout = setTimeout(function(){

            timeout = null;

            func.apply(context, args)

        }, wait)

    }

}

}
我们可以看到:当鼠标移入的时候,事件不会立刻执行,晃了 3s 后终于执行了一次,此后每 3s 执行一次,当数字显示为 3 的时候,立刻移出鼠标,相当于大约 9.2s 的时候停止触发,但是依然会在第 12s 的时候执行一次事件。

所以比较两个方法:

第一种事件会立刻执行,第二种事件会在 n 秒后第一次执行

第一种事件停止触发后没有办法再执行事件,第二种事件停止触发后依然会再执行一次事件

双剑合璧

那我们想要一个什么样的呢?

有人就说了:我想要一个有头有尾的!就是鼠标移入能立刻执行,停止触发的时候

还能再执行一次!

所以我们综合两者的优势,然后双剑合璧,写一版代码:

// 第三版

function throttle(func, wait) {
   

    var timeout, context, args, result;

    var previous = 0;

    var later = function() {
   

        previous = +new Date();

        timeout = null;

        func.apply(context, args)

    };

    var throttled = function() {
   

        var now = +new Date();

        //下次触发 func 剩余的时间

        var remaining = wait - (now - previous);

        context = this;

        args = arguments;

        // 如果没有剩余的时间了或者你改了系统时间

        if (remaining <= 0 || remaining > wait) {
   

            if (timeout) {
   

                clearTimeout(timeout);

                timeout = null;

            }

            previous = now;

            func.apply(context, args);

        } else if (!timeout) {
   

            timeout = setTimeout(later, remaining);

        }

    };

    return throttled;

}
我们可以看到:鼠标移入,事件立刻执行,晃了 3s,事件再一次执行,当数字

变成 3 的时候,也就是 6s 后,我们立刻移出鼠标,停止触发事件,9s 的时

候,依然会再执行一次事件。

优化

但是我有时也希望无头有尾,或者有头无尾,这个咋办?

那我们设置个 options 作为第三个参数,然后根据传的值判断到底哪种效果,

我们约定:

leading:false 表示禁用第一次执行

trailing: false 表示禁用停止触发的回调

我们来改一下代码:

// 第四版

function throttle(func, wait, options) {
   

    var timeout, context, args, result;

    var previous = 0;

    if (!options) options = {
   };

    var later = function() {
   

        previous = options.leading === false ? 0 : new Date().getTime();

        timeout = null;

        func.apply(context, args);

        if (!timeout) context = args = null;

    };

    var throttled = function() {
   

        var now = new Date().getTime();

        if (!previous && options.leading === false) previous = now;

        var remaining = wait - (now - previous);

        context = this;

        args = arguments;

        if (remaining <= 0 || remaining > wait) {
   

            if (timeout) {
   

                clearTimeout(timeout);

                timeout = null;

            }

            previous = now;

            func.apply(context, args);

            if (!timeout) context = args = null;

        } else if (!timeout && options.trailing !== false) {
   

            timeout = setTimeout(later, remaining);

        }

    };

    return throttled;

}

取消
在 debounce 的实现中,我们加了一个 cancel 方法,throttle 我们也加个 cancel 方法:

// 第五版 非完整代码,完整代码请查看最后的演示代码链接

throttled.cancel = function() {
   

    clearTimeout(timeout);

    previous = 0;

    timeout = null;

}

注意
我们要注意 underscore 的实现中有这样一个问题:

那就是 leading:false 和 trailing: false 不能同时设置。 如果同时设置的话,比如当你将鼠标移出的时候,因为 trailing 设置为false, 停止触发的时候不会设置定时器,所以只要再过了设置的时间,再移入的话,就会 立刻执行,就违反了 leading: false,bug 就出来了,所以,这个throttle 只有三种用法:

container.onmousemove = throttle(getUserAction, 1000);

container.onmousemove = throttle(getUserAction, 1000, {
   

    leading: false

});

container.onmousemove = throttle(getUserAction, 1000, {
   

    trailing: false

});
相关文章
|
12月前
|
数据中心
|
Kubernetes Cloud Native 应用服务中间件
【云原生】使用k8s创建nginx服务—通过yaml文件svc类型暴露
【云原生】使用k8s创建nginx服务—通过yaml文件svc类型暴露
559 0
|
12月前
|
运维 Cloud Native 持续交付
云原生架构的演进与实践####
【10月更文挑战第16天】 云原生,这一概念自提出以来,便以其独特的魅力和无限的可能性,引领着现代软件开发与部署的新浪潮。本文旨在探讨云原生架构的核心理念、关键技术及其在实际项目中的应用实践,揭示其如何帮助企业实现更高效、更灵活、更可靠的IT系统构建与管理。通过深入剖析容器化、微服务、持续集成/持续部署(CI/CD)等核心技术,结合具体案例,本文将展现云原生架构如何赋能企业数字化转型,推动业务创新与发展。 ####
273 47
|
12月前
|
JavaScript 前端开发
阻止事件冒泡与捕获
【10月更文挑战第15天】
|
12月前
|
人工智能 分布式计算 大数据
Linux操作系统:开源力量的崛起与影响###
一场技术革命的火种,如何燎原? 本文将带您深入探索Linux操作系统的诞生背景、核心特性及其对现代科技世界的深远影响。从1991年芬兰学生Linus Torvalds的一个小众项目,到如今成为支撑全球无数服务器、超级计算机及物联网设备的基石,Linux的发展既是一部技术创新史,也是开源文化胜利的见证。通过剖析其设计哲学、安全性、灵活性等关键优势,结合实例展示Linux在云计算、大数据处理等领域的广泛应用,本文旨在揭示Linux为何能在众多操作系统中脱颖而出,以及它如何塑造了我们今天的数字生活。 ###
|
12月前
|
移动开发 编解码 数据可视化
低代码可视化-uniapp SliderRange区间组件-代码生成器
SliderRange区间组件是一种用户界面元素,允许用户通过拖动滑块选择数值范围。组件支持微信小程序、H5和App,具有高度可定制性、响应式设计和多种事件处理功能。适用于价格筛选、音量调节等场景。代码实现包括滑动区域、滑块、事件处理等部分,支持可视化配置步长、颜色等属性。使用时需注意选择合适步长、提供清晰标签和考虑无障碍设计。
328 0
|
12月前
|
运维 网络性能优化 网络虚拟化
|
12月前
|
SQL 负载均衡 关系型数据库
构建高效的后端服务:从设计到部署
【10月更文挑战第16天】 在当今的数字化时代,后端服务的效率和可靠性对于任何成功的在线业务至关重要。本文将探讨如何设计和部署一个高效的后端服务,包括选择合适的技术栈、优化数据库性能、实现负载均衡以及确保安全性。我们将通过具体的案例分析,展示这些策略如何在实际中应用,并提供一些实用的技巧和最佳实践。
208 50
|
10月前
|
人工智能 运维 架构师
开始报名,龙蜥社区系统运维联盟MeetUp暨iAutoBASE专题论坛来啦
12月27日,探讨车用基础软件技术及生态发展,欢迎报名。
开始报名,龙蜥社区系统运维联盟MeetUp暨iAutoBASE专题论坛来啦
|
12月前
|
Web App开发 缓存 测试技术