节流(Throttling)和去抖(Debouncing)详解
这篇文章的作者是 David Corbacho,伦敦的一名前端开发工程师。之前我们有一篇关于”节流”和”去抖”的文章:The Difference Between Throttling and Debouncing(译文:节流(Throttling)和去抖(Debouncing)的区别),但是David的这篇文章通过一些可交互的Demo来给我们做了一个更详细的解释。
“节流”和”去抖”都是用来限制一些需要一直执行的函数的技术,它们虽然很相似,但它们是不一样的。
当我们需要做一些DOM事件绑定时 ,”节流”和”去抖”是非常有用的。因为这样的话,我们在事件和事件函数之间又多了一层控制。所以,我们控制的不是事件的触发(事件触发的时机和频率),而是事件触发后去限制事件函数的执行。
例如我们非常常用的滚动事件:
当我们在电脑上进行滚动(用触控板、鼠标滚轮、拉动滚动条)的话,事件的触发可能会比较好控制一些,比如我想让它每秒触发30次,那么我滚动的慢一点,是可以达到的。但是,如果我在移动设备上(比如智能手机,iPad等设备)上进行滑动的话,每秒钟会触发100次左右。这很显然不是我们想要的结果,因为事件函数执行了太多次了。
在2011年,Twitter的网站出现过一个问题:当你滚动Twitter网站到底部进行加载数据时,页面将会变的非常卡,有时甚至未响应。John Resig写了一篇Learning from Twitter来说明了这个问题。
John当年(2011年)给出的解决方案是在滚动事件的外面以250毫秒为单位进行循环执行函数,这样函数和事件之间就实现了松耦合。用了这个方法,至少就不会影响到正常的用户体验了。
而现在,我们有一些更好更精细的方法来处理这个问题。下面,就让我们来介绍一下”去抖”、”节流”和”requestAnimationFrame()
“方法。我们将通过对应的案例来进行详细的说明。
去抖
“去抖”可以让我们把一个连续的的函数调用”打包”成一个。
举个例子,假如你正在坐电梯,电梯门即将关上,这时突然有一个人想上电梯,于是电梯的门又开了。随后又来了一个人,同样的事又发生了一遍。电梯的根本功能是将你带到其它的楼层,而现在它由于很多人按电梯,所以到其它楼层这件事儿就被延误了,但是这样设计的原因是为了最终能节省资源。
你可以通过下面的例子来感受一下,点击下面的按钮或在上面进移动:
从上面的Demo中可以看到,”去抖”是如何工作的,它把本来一系列的事件触发变成了一个。但同时你也会发现,当我们的事件一直被触发时(例如上面的例子当中,用鼠标不停的在按钮上滑动),”去抖”就不起作用了。
‘去抖’前沿(去抖刚开始的时候)触发
“去抖”后函数只会在事件结束时触发,你可能会觉得”去抖”前的那段等待时间是非常不必要的。可是如果在一开始就触发函数那不是跟没有”去抖”一样了吗?其实不然,我们可以让函数在”去抖”的一开始就执行一次。
下面是’前沿’选项打开后的效果:
在 underscore.js
当中,可以通过把选项当中的leading
换成immediate
来实现。
(译者:在本段的上面和下面的例子当用的并非underscore,而是lodash)
下面是用lodash打开”前沿”触发后的效果:
‘去抖’的实现
我第一次见到在JS中实现”去抖”是John Hann在2009年写这篇Debouncing Javascript Methods。
之后不久,Ben Alman编写了一个jQuery插件(该插件已经很久没更新了),一年后,Jeremy Ashkenas又将其加入了underscore.js,随后loadsh也加入了该功能。
在上面所说的几种实现方式中,它们代码内部可能会有点小区别,但是使用方式都是差不多的。
以前underscore采用了和lodash一样的实现方式,但在2013年我发现了一个BUG。从那以后,它们则各自有自己的实现方式了。
Lodash 添加了一些其它功能在它的_.debounce
和_.throttle
方法中。原来的immediate
属性也被替换成了leading
和trailing
。默认情况下,只有trailing
是开启状态。
新的maxWait
属性(目前只在Lodash当中有)在本文章当中没有说到,但它是一个非常有用的选项。实际上,throttle
方法在内部就是调用了带有maxWait
参数的_.debounce
来实现的,你可以去看看lodash源代码。
“去抖”实例
缩放实例
当缩放浏览器窗口大小的时候,会触发很多次resize
事件。
上面的例子中,我们针对resize
事件用了默认的选项(开启trailing
),因为我们只关心缩放结束后的窗口大小。
“自动完成”(键盘按下时进行Ajax请求)实例
当用户进行键盘输入操作时就进行发送Ajax请求,这是不合理的,因为这样可能大概50毫秒就会发送一次请求。_.debounce
方法会帮我们解决这个问题,用了该方法后,只有当我们停止输入的时候才会向后台发送Ajax请求。
这个例子也是不需要开启leading
的。因为我们想要的是用户在输完最好一个字母时才触发函数。
如何使用”去抖”和”节流”以及它们的一些常见问题
我建议你使用underscore
或者Lodash
。如果你需要_.debounce
和_.throttle
方法,你可以下载Lodash的自制版本,只需要一些简单的命令即可:
npm i -g lodash-cli
lodash-cli include=debounce,throttle
有一个常见的问题就是使用_.debounce
两次:
// WRONG
$(window).on('scroll', function() {
_.debounce(doSomething, 300);
});
// RIGHT
$(window).on('scroll', _.debounce(doSomething, 200));
如果用一个变量把”去抖”后的函数存储下来,那么我们可以通过调用debounced_version.cancel()
来关闭这个”去抖”,在lodash
和underscore.js
中,你可以这样用。
var debounced_version = _.debounce(doSomething, 200);
$(window).on('scroll', debounced_version);
// 如果有需要的话
debounced_version.cancel();
节流
使用_.throttle
方法后会限制一个不得不需要一直执行的函数的执行频率,比如限制它每x
毫秒执行一次。
其实,”节流”和”去抖”的最大区别就是,”节流”会保证函数一直在有规律的执行(至少每x
毫秒一次的频率进行执行)。
和debounce
一样,throttle
方法也集成在了underscore.js
和lodash
当中。
‘节流’实例
‘无限滚动’实例
这是一个很常见的例子,就是当用户将页面滚动到将近底部时,发送一个Ajax请求,然后返回的数据添加到后面进行显示。
这个时候,上面所说到的_.debounce
就不适用了,如果使用_.debounce
的话,那么它只会在用户停止滚动时触发。但我们需要的是当用户快达到底部时触发。
使用_.throttle
,我们可以保证一直监测用户滚动到了哪里。
requestAnimationFrame (rAF)
requestAnimationFrame
是另一种限制函数执行频率的方法。
rAF就像_.throttle(dosomething, 16).
方法。但是它更精确,因为它是浏览器内置的API。
我们可以使用rAF
来替代throttle
,下面来看一下它的优点和缺点。
优点:
- rAF的刷新频率是`60fps`(每16毫秒刷新一次),但其实在内部,这个数字是不确定的,它会在适当的时候调为到比较适当的频率来进行渲染
- 非常简单的API,一看就会
缺点:
- rAFs的开始和结束是需要我们自己手动完成的,不像`.debounce`和`.throttle`会在内部自动进行完成
- 如果浏览器窗口此时是未激活状态,它将不会执行。
- 即可一些现代浏览器都支持了rAF,但是IE9、Opera Mini以及一些旧的安卓浏览器是不支持的。
- nodeJS不支持rAF。
一般说来,rAF用在绘图和动画当中较多一点。如果是进行Ajax请求,或者进行添加、删除class(并会有一些CSS动画),那么用_.debounce
或_.throttle
会更好。
rAF实例
下面的例子中,我将rAF和设置了参数为16ms
的_.throttle
进行了对比( Paul Lewis的这篇Leaner, Meaner, Faster Animations with requestAnimationFrame有更详细的解释)。
结语
总之,你可以使用debounce
,throttle
和requestAnimationFrame
去优化你的函数执行。每种方法都有一些细微的差别,但它们都非常有用。没有最好用的方法,只有更好用的方法。
- 去抖-debounce:把一个一连串的事件函数(例如键盘输入)”打包”成一个进行执行
- 节流-throttle:保证你的函数在一定频率下一直执行。例如当你滚动页面时,它会保证在每200ms检测一下你滚动的位置。
- requestAnimationFrame: 可以看做是`throttle`的替代品。当你的函数有很多的动画渲染或者有很多的元素操作时,你想保证动画的流畅性,就需要用到这个。注意:IE9不支持rAF.
节流(Throttling)和去抖(Debouncing)详解的更多相关文章
- JavaScript 函数节流和函数去抖应用场景辨析
概述 也是好久没更新 源码解读,看着房价蹭蹭暴涨,心里也是五味杂陈,对未来充满恐惧和迷茫 ...(敢问一句你们上岸了吗) 言归正传,今天要介绍的是 underscore 中两个重要的方法,函数节流和函 ...
- javascript中的函数节流和函数去抖
带着问题去尝试 首先我们要知道为什么要用到函数节流和函数去抖?我们带着以下的疑问来进行分析! 1.比如搜索框,你会用到什么事件(change.blur.keyup等)?去做什么效果?2.再比如scro ...
- js 函数节流throttle 函数去抖debounce
1.函数节流throttle 通俗解释: 假设你正在乘电梯上楼,当电梯门关闭之前发现有人也要乘电梯,礼貌起见,你会按下开门开关,然后等他进电梯: 但是,你是个没耐心的人,你最多只会等待电梯停留一分钟: ...
- JavaScript 中函数节流和函数去抖的讲解
JavaScript 中函数节流和函数去抖的讲解 我们都知道频繁触发执行一段js逻辑代码对性能会有很大的影响,尤其是在做一些效果实现方面,或者逻辑中需要进行后端请求,更是会导致卡顿,效果失效等结果,所 ...
- JavaScript函数节流与函数去抖
介绍 首先解释一下这两个概念: 函数节流(throttle):是让一个函数无法在很短的时间间隔内连续调用,当上一次函数执行后过了规定的时间间隔,才能进行下一次该函数的调用. 函数去抖(debounce ...
- 【js】什么是函数节流与函数去抖
函数节流 意思:节省流量,不会一直访问. | 指定时间内不执行,指定时间后执行. | 一段时间内只执行一次 场景: 比如控制游戏人物攻击,时间内就算按得很快,也只能砍一刀,过后才能砍第二刀. 搜索引擎 ...
- Apk去签名校验详解
某些apk为了防止重打包,使用了签名校验.所以在破解的时候我们需要破解签名校验.在定位签名校验位置时常用的关键词有sign,signature,checkSign,signCheck,getPacka ...
- python接口自动化(三十)--html测试报告通过邮件发出去——中(详解)
简介 上一篇,我们虽然已经将生成的最新的测试报告发出去了,但是MIMEText 只能发送正文,无法带附件,因此我还需要继续改造我们的代码,实现可以发送带有附件的邮件.发送带附件的需要导入另外一个模块 ...
- JavaScript 函数节流和函数去抖
概念 函数防抖(debounce) 当调用动作过n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间 函数节流(throttle) 预先设定一个执行周期,当调用动作的时刻大于等于执 ...
随机推荐
- ansible playbook 变量
变量优先级 在命令中定义的变量(-e参数指定的) 在inventory中定义的变量(ansible_ssh_user等) 其他变量(role中.play中) 系统通过father_facts定义的变量 ...
- JavaEE之JDBC编程[详解]
1.数据库简介 数据库(DB,Data Base ) 数据库管理系统(DBMS,Data Base Management System) 关系型数据库(RDB) 关系型数据库管理系统(RDBMS) S ...
- tomcat源码研究之源码导入eclipse
版本:8.5.x官网:https://tomcat.apache.org/svn.html1. 下载源码,git镜像:https://github.com/apache/tomcat85 .2. 导入 ...
- jquery 禁止滚动条滚动,并且滚动条不消失,页面大小不闪动
一,禁止滚动,滚动条不消失,页面大小不闪动 //禁止滚动条滚动 function unScroll() { var top = $(document).scrollTop(); $(document) ...
- parseDouble()方法
String a = "269"; double parseDouble = Double.parseDouble(b); System.out.println(“Double a ...
- ASP.NET Web API 2 过滤器
Ø 前言 我们知道 ASP.NET Web API 过滤器,也是属于消息处理机制中的一部分.正因如此,我们经常使用它来完成对请求的授权验证.参数验证,以及请求的 Log 记录,程序异常捕获等. 1. ...
- lua 设置文件运行的环境
背景 在一个lua文件中书写的代码, 使用的变量, 需要设置其运行环境. 目的: 1. 不破坏全局环境. 2. 限定文件所使用的环境, 作为沙箱功能. 解法 限定运行空间环境的文件: local m ...
- Spring Boot 起步
……………………………………………………………………………………………………………… [应用配置]application.yml [port][context-path][datasource][jp ...
- window中安装mongodb
转自:https://blog.csdn.net/heshushun/article/details/77776706 一.先登录Mongodb官网https://www.mongodb.com/do ...
- LinkedQueue的底层实现
package zy813ture; public class MyLinkedQueue1 { private Node head; private Node last = head = new N ...