【JavaScript】面试手撕节流

引入

上篇我们讲了防抖,这篇我们就谈谈防抖的好兄弟 – 节流。这里在老生常谈般的提一下他们两者之间的区别,顺带给读者巩固下。

PS: 开源节流中节流与这个技术上的节流,个人认为本质上是一样的。

  • 开源节流的节流指的是节省公司的金钱开支。
  • 前端技术上的节流指的是稀释函数的调用频率,节省CPU的开支。

区别

  • 节流: N 秒内只运行一次,若在N秒内重复触发,只有第一次生效
  • 防抖: N 秒后在执行该事件,若在N秒内被重复触发,则重新计时

不过我认为还是防抖那篇文章有个读者的评论更显生动 🐶, 在此对该读者表示感谢🙏。

  • 节流: 可以看做攻击间隔,点的再快没打出来也不会同时攻击两次。
  • 防抖: 可以理解为回城,每点一下就要重新跑.

节流例子

这里我举两个常见的🌰,大家有什么更好的🌰可以在评论里回复
这个王者荣耀的攻击例子也算是节流,不过这个我就不举了。🐶

生活例子

这里跟大家举一个生活例子,更显生动。

我们知道一般女神回复舔🐶的概率都是比较低的,假设女神每天回复舔🐶一次。也就是说如果今天女生已经回复过了,那么无论舔🐶发再多的信息对方也是不会回的。今天的次数已经消耗完毕了,也只能等明天才能刷新。

短信验证

我们知道短信验证是在生活中很常见,其实短信验证便用到了节流的技术。
因为发短信其实是要钱的,为了避免一个用户重复点击导致发出多条短信就要使用节流。比如获取一次验证码的有效时间为60秒,则60秒内这个发送短信的方法不能再次触发。

手撕代码

xdm,接下来开始手撕代码了。这里有两种实现方式,分别是时间戳实现和定时器实现。

时间戳实现

原理

每次事件触发都会检查距离上次执行的时间间隔,如果超过指定的等待时间delay,则执行函数。并且更新上一次执行时间为为当前的时间戳。

代码
function throttleByTimestamp(fn, wait) {
  let lastTime = 0; // 用于保存上一次执行的时间戳

  return function () {
    const currentTime = new Date().getTime();
    // 判断当前时间与上次执行时间差是否大于等待时间
    if (currentTime - lastTime > wait) {
      // 如果满足条件,则执行原函数,并传递参数
      fn.apply(this, arguments);
      // 更新上一次执行的时间戳为当前时间
      lastTime = currentTime;
    }
  };
}

// 使用
const throttledFn = throttleByTimestamp(function () {
  console.log("节流函数被执行");
}, 500); // 每隔500毫秒执行一次

// 等待时间
const waitTime = async (time) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, time);
  });
};

const main = async() => {
    throttledFn();
    // 换成 < 500ms的则只会执行一次
    await waitTime(600);
    throttledFn();
}

main()

/**
 * 输出:
 * 节流函数被执行
 * 节流函数被执行
 */

定时器实现

原理

我们在事件触发时设置一个定时器。

  • 首次事件触发时,我们设置一个定时器,在等待一段时间后执行函数。
  • 当定时器触发前,如果有新的事件触发,我们会检查是否存在定时器,如果存在则跳过。
  • 当定时器触发时,会清除当前定时器,确保下一次事件能重新触发。
代码

注: 测试例子跟上面的一样,此处不在贴啦。

function throttleByTimer(fn, delay) {
  let timer;
  return function () {
    const context = this;
    const args = arguments;
    if (!timer) {
      timer = setTimeout(() => {
        fn.apply(context, args);
        clearTimeout(timer);
        timer = null;
      }, delay);
    }
  };
}

时间戳+定时器实现

我们回顾这两个实现方式,大家有没有发现这两个实现方式的区别?

  • 时间戳: 因为是拿当前时间减上一次触发的时间,所以一旦满足该条件,事件会立即执行。
  • 定时器: 由于fn.apply的函数写在了setTimeout里,所以触发了,也得等delay后才能执行,于是就有了这种事件停止触发后依然会再一次执行的效果。

如果我们要实现这样的一个需求,完成一个事件触发时立即执行,触发完毕还能执行一次的节流函数该如何做呢?

原理

此处借鉴掘金网友《6个瑞士卷》的分析,个人觉得逻辑写的很好。于是摘录了下来。

  • 需要在每个delay时间中一定会执行一次函数,因此在节流函数内部使用开始时间、当前时间与delay来计算remaining
  • remaining <= 0时表示该执行函数了,如果还没到时间的话就设定在remaining时间后再触发。
  • 当然在remaining这段时间中如果又一次发生事件,那么会取消当前的计时器,并重新计算一个remaining来判断当前状态。
代码
function throttle(fn, delay) {
  let timer;
  let lastTime = 0;

  return function () {
    let currentTime = Date.now();
    // 计算距离上次执行fn到现在过去了多少时间,与delay做比较
    let remaining = delay - (currentTime - lastTime);
    const context = this;
    const args = arguments;

    // 如果在remaining这段时间在发生,会取消当前的计时器
    clearTimeout(timer);
    // 当remaining <= 0时表示该执行函数了
    if (remaining <= 0) {
      fn.apply(context, args);
      lastTime = Date.now();
    } else {
      // 如果还没到时间的话就设定在remaining时间后再触发
      timer = setTimeout(fn, remaining);
    }
  };
}

借鉴文章

  1. JS简单实现防抖和节流

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/420164.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Windows的Docker-Desktop安装与问题总结

目录 Docker-Desktop安装步骤 环境配置 Docker-Desktop安装问题总结 问题1&#xff1a;docker-desktop setting界面一直加载转圈 问题2&#xff1a;docker镜像的存储位置变更&#xff08;防止C盘空间不足&#xff09; 参考文献&#xff1a; Docker-Desktop安装步骤 环境…

Unity(第十八部)物理力学,碰撞,触发、关节和材质

1、重力 刚体组件 英文中文描述RigidBody刚体组件physics->rigidbody &#xff0c;刚体组件使一个物体有了质量&#xff0c;重力等。&#xff0c;use gravity 勾选后&#xff0c;物体才会受到重力&#xff0c;会自动下落&#xff0c;取消勾选就不会。&#xff0c;&#xf…

计算机网络物理层知识点总结

本篇博客是基于谢希仁编写的《计算机网络》和王道考研视频总结出来的知识点&#xff0c;本篇总结的主要知识点是第二章的物理层。上一章的传送门&#xff1a;计算机网络体系结构-CSDN博客 通信基础 物理层概念 物理层解决如何在连接各种计算机的传输媒体上传输数据比特流&am…

ElasticSearch搜索引擎使用指南

一、ES数据基础类型 1、数据类型 字符串 主要包括: text和keyword两种类型&#xff0c;keyword代表精确值不会参与分词&#xff0c;text类型的字符串会参与分词处理 数值 包括: long, integer, short, byte, double, float 布尔值 boolean 时间 date 数组 数组类型不…

汽车三元催化器的废品项目详解,三元催化再生项目的回收技术教学

一、教程描述 这是一个收废品项目&#xff0c;就收那些别人不懂的&#xff0c;三元催化器的附加值高&#xff0c;只要掌握技术&#xff0c;怎么玩都行的&#xff0c;只是要放得下你的面子。三元催化器&#xff0c;是安装在汽车排气系统中最重要的机外净化装置&#xff0c;它可…

CodeWhisperer安装教导--一步到位!以及本人使用Whisperer的初体验。

CodeWhisperer是亚马逊出品的一款基于机器学习的通用代码生成器&#xff0c;可实时提供代码建议。类似 Cursor 和Github AWS CodeWhisperer 亚马逊科技的CodeWhisperer是Amazon于2021年12月推出的一款代码补全工具&#xff0c;与GitHub Copilot类似。主要的功能有:代码补全注释…

网络编程学习

思维导图 代码练习 TCP实现通信 服务器端代码 #include <myhead.h> #define SER_IP "192.168.152.135" #define SER_PORT 8910 int main(int argc, const char *argv[]) {//&#xff11;创建用于监听的套接字int sfd -1;sfd socket(AF_INET,SOCK_STREAM,0)…

python中自定义报错

class MyError(Exception):def __init__(self,num):#录入的数Exception.__init__(self)self.numnumdef __str__(self):return 这是我定义的第%d个异常 %(self.num)使用 try:raise MyError(4) except MyError as e:print(e)raise 其作用是指定抛出的异常名称&#xff0c;以及异常…

【c++】通讯录管理系统

1.系统功能介绍及展示 2.创建项目 3.菜单实现 4.退出功能实现 5.添加联系人—结构体设计 6.添加联系人—功能实现 7.显示联系人 8.删除练习人—检测联系人是否存在 9.删除联系人—功能实现 10.查找联系人 11.修改联系人 12.清空通讯录 #include <iostream> #include <…

JavaScript类型转换

一些需要注意的数据类型&#xff1a; NaN的数据类型是numberArray、Date、null的数据类型是object未定义变量的数据类型是undefined 自动转换类型&#xff1a;尝试操作一个 “错误” 的数据类型时&#xff0c;会自动转换为 “正确” 的数据类型。 5 null // 返回 5 …

百度测试经理,对自动化测试初学者的一些忠告

前言 相信很多的测试人员都有这样的顾虑&#xff0c;初学自动化测试应该怎么去做&#xff0c;那么现在我就把我在百度测试岗学到的经验分享给大家&#xff0c;希望对你们有帮助。 为了大家在学习的道路上更加轻松&#xff0c;我还给大家整理了一套Python自动化测试学习资料以…

Vue全家桶:vue2+vue3全部搞懂:第五篇,Vue的watch监视器

前提&#xff0c;建议先学会前端几大基础&#xff1a;HTML、CSS、JS、Ajax&#xff0c;不然不好懂 这一专栏知识将一次性将vue、vue2、vue3全部讲明白 一、何为watch监视器 其实我个人理解&#xff0c;就跟原本的表单的input事件一样&#xff0c;实时监视事件发生并同步更新数…

RuoYi-Vue-Plus功能分析-jackson配置

文章目录 前言一、配置文件二、配置类三、注解四、json工具类1. 工具内容2. 使用工具 前言 前端在给我发送请求的时候一般包含三个部分url&#xff0c;header&#xff0c;body。那么就会涉及我们后端如何接收这些请求参数并且我们处理完毕参数后前端又如何接收参数 通过url传…

【python】json转成成yaml中文编码异常显示成:\u5317\u4EAC\u8DEF123\u53F7

姊妹篇&#xff1a;【python】json转成成yaml json数据 {"name": "张三","age": 30,"isMarried": false,"children": [{"name": "小王","age": 5},{"name": "小李",&qu…

装饰器模式 详解 设计模式

装饰器模式 它允许你在不改变对象结构的情况下&#xff0c;动态地将新功能附加到对象上。 结构&#xff1a; 抽象组件&#xff08;Component&#xff09;&#xff1a;定义了原始对象和装饰器对象的公共接口或抽象类&#xff0c;可以是具体组件类的父类或接口。具体组件&…

算法--动态规划(线性DP、区间DP)

这里写目录标题 tip数组下标从0开始还是从1开始 线性DP数学三角形介绍算法思想例题代码 最长上升子序列介绍算法思想例题代码 最长公共子序列介绍算法思想例题代码 编辑距离介绍例题代码 区间DP问题石子合并介绍算法思想例题代码 tip 数组下标从0开始还是从1开始 如果代码中涉…

Topaz Video AI:一键提升视频品质,智能重塑影像魅力 mac/win版

Topaz Video AI是一款革命性的视频智能处理软件&#xff0c;它利用先进的机器学习和人工智能技术&#xff0c;为视频创作者提供了前所未有的视频增强和修复功能。无论您是专业视频编辑师、摄影师&#xff0c;还是热爱视频创作的爱好者&#xff0c;Topaz Video AI都能帮助您轻松…

返回JSON对象

在目前的Java项目中&#xff0c;我们最经常使用的便是JSON&#xff0c;不是传递JSON对象&#xff0c;就是返回JSON对象&#xff0c;甚至还把多个参数封装成JSON对象来进行传递&#xff0c;以便简化代码等&#xff01; 但是&#xff0c;该如何操作代码才能正确的返回一个或者多…

excel导出标准化

虽然标题叫标准化&#xff0c;只不过是我自己的习惯&#xff0c;当一件事情变得流程标准化之后&#xff0c;开发程序就会飞快&#xff0c;开发评估工作总是 搞个1~2天&#xff0c;实则前端后端一起开发&#xff0c;1个小时就可以搞定。 1 前端 const exportXls async () >…

每日一题——LeetCode1556.千位分隔符

方法一 个人方法&#xff1a; 把n转为字符串&#xff0c;逆序遍历n&#xff0c;把n的每个元素加入res&#xff0c;每三次加入.&#xff0c;最后将res翻转再转为字符串即为符合题目要求的结果 var thousandSeparator function(n) {nlet res[],lenn.length-1for(let ilen;i>…