节流(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) 预先设定一个执行周期,当调用动作的时刻大于等于执 ...
随机推荐
- Golang的排序和查找
Golang的排序和查找 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.排序的基本介绍 排序是将一组数据,依指定的顺序进行排列的过程.排序的分类如下 1>.内部排序 指将 ...
- MyEclipse项目中,让修改后的Servlet文件立即运行生效方法
运行的时候用Debug模式发布如图 java 自动生成get set方法的快捷键是什么? Re:在myeclipse中按住shift+alt+s选择 generate getters and sett ...
- javascript中click和onclick的区别
<script type="text/javascript"> $(function(){ $("#btn4").click(function(){ ...
- 关于Mysql的高级查询的操作
前言:作为一名后端的程序员操作数据库的能力是我们基本的技能,而连表查询是我们的这个技能的关键点所在.注意这里顾明思义是对数据的查询的操作 (一).联合查询(关键字union/union all) 什么 ...
- Django之组件--中间件
中间件 中间件是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出.因为改变的是全局,所以需要谨慎实用,用不好会影响到性能 自定义中间 ...
- 简述react与vue的区别
React 和Vue是现在主流的两个框架(相对来说angular用的已经少了) 两者的区别体现在以下方面 相同点: 1.react和vue都支持服务端渲染 2.都有虚拟DOM,组件化开发,通过prop ...
- Basic berkeley socket functions
gethostbyname() DNS を通して.Domain の Information を GET する.例えば IP Address なんだ. げん型: #include <netdb.h ...
- 解决XP系统任务管理器显示不全
我们在使用电脑的时候有的时候打开任务管理器会发现任务管理器显示不全. 当碰到这种情况怎么解决呢?任务管理器显示不全的原因又是那些呢? 这里就来为大家分享下为什么任务管理器会显示不全以及如何解决这个问题 ...
- 经典文摘:饿了么的 PWA 升级实践(结合Vue.js)
自 Vue.js 官方推特第一次公开到现在,我们就一直在进行着将饿了么移动端网站升级为 Progressive Web App 的工作.直到近日在 Google I/O 2017 上登台亮相,才终于算 ...
- datatable转换为list<model> 映射
using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.R ...