这篇文章的作者是 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属性也被替换成了leadingtrailing。默认情况下,只有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()来关闭这个”去抖”,在lodashunderscore.js中,你可以这样用。

var debounced_version = _.debounce(doSomething, 200);
$(window).on('scroll', debounced_version); // 如果有需要的话
debounced_version.cancel();

节流

使用_.throttle方法后会限制一个不得不需要一直执行的函数的执行频率,比如限制它每x毫秒执行一次。
其实,”节流”和”去抖”的最大区别就是,”节流”会保证函数一直在有规律的执行(至少每x毫秒一次的频率进行执行)。
debounce一样,throttle方法也集成在了underscore.jslodash当中。

‘节流’实例

‘无限滚动’实例

这是一个很常见的例子,就是当用户将页面滚动到将近底部时,发送一个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,throttlerequestAnimationFrame去优化你的函数执行。每种方法都有一些细微的差别,但它们都非常有用。没有最好用的方法,只有更好用的方法。

    • 去抖-debounce:把一个一连串的事件函数(例如键盘输入)”打包”成一个进行执行
    • 节流-throttle:保证你的函数在一定频率下一直执行。例如当你滚动页面时,它会保证在每200ms检测一下你滚动的位置。
    • requestAnimationFrame: 可以看做是`throttle`的替代品。当你的函数有很多的动画渲染或者有很多的元素操作时,你想保证动画的流畅性,就需要用到这个。注意:IE9不支持rAF.

节流(Throttling)和去抖(Debouncing)详解的更多相关文章

  1. JavaScript 函数节流和函数去抖应用场景辨析

    概述 也是好久没更新 源码解读,看着房价蹭蹭暴涨,心里也是五味杂陈,对未来充满恐惧和迷茫 ...(敢问一句你们上岸了吗) 言归正传,今天要介绍的是 underscore 中两个重要的方法,函数节流和函 ...

  2. javascript中的函数节流和函数去抖

    带着问题去尝试 首先我们要知道为什么要用到函数节流和函数去抖?我们带着以下的疑问来进行分析! 1.比如搜索框,你会用到什么事件(change.blur.keyup等)?去做什么效果?2.再比如scro ...

  3. js 函数节流throttle 函数去抖debounce

    1.函数节流throttle 通俗解释: 假设你正在乘电梯上楼,当电梯门关闭之前发现有人也要乘电梯,礼貌起见,你会按下开门开关,然后等他进电梯: 但是,你是个没耐心的人,你最多只会等待电梯停留一分钟: ...

  4. JavaScript 中函数节流和函数去抖的讲解

    JavaScript 中函数节流和函数去抖的讲解 我们都知道频繁触发执行一段js逻辑代码对性能会有很大的影响,尤其是在做一些效果实现方面,或者逻辑中需要进行后端请求,更是会导致卡顿,效果失效等结果,所 ...

  5. JavaScript函数节流与函数去抖

    介绍 首先解释一下这两个概念: 函数节流(throttle):是让一个函数无法在很短的时间间隔内连续调用,当上一次函数执行后过了规定的时间间隔,才能进行下一次该函数的调用. 函数去抖(debounce ...

  6. 【js】什么是函数节流与函数去抖

    函数节流 意思:节省流量,不会一直访问. | 指定时间内不执行,指定时间后执行. | 一段时间内只执行一次 场景: 比如控制游戏人物攻击,时间内就算按得很快,也只能砍一刀,过后才能砍第二刀. 搜索引擎 ...

  7. Apk去签名校验详解

    某些apk为了防止重打包,使用了签名校验.所以在破解的时候我们需要破解签名校验.在定位签名校验位置时常用的关键词有sign,signature,checkSign,signCheck,getPacka ...

  8. python接口自动化(三十)--html测试报告通过邮件发出去——中(详解)

    简介 上一篇,我们虽然已经将生成的最新的测试报告发出去了,但是MIMEText 只能发送正文,无法带附件,因此我还需要继续改造我们的代码,实现可以发送带有附件的邮件.发送带附件的需要导入另外一个模块 ...

  9. JavaScript 函数节流和函数去抖

    概念 函数防抖(debounce) 当调用动作过n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间 函数节流(throttle) 预先设定一个执行周期,当调用动作的时刻大于等于执 ...

随机推荐

  1. Linux_问题

    1.远程连接项目服务器(miit_shi),遇到的问题 问题:Connecting to IP地址:22... Connection established. To escape to local s ...

  2. MySQL权限授权认证详解

    MySQL权限授权认证详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.MySQL权限系统介绍1>.权限系统的作用是授予来自某个主机的某个用户可以查询.插入.修改.删除 ...

  3. JAVA核心技术I---JAVA基础知识(集合set)

    一:集合了解 (一)确定性,互异性,无序性 确定性:对任意对象都能判定其是否属于某一个集合 互异性:集合内每个元素都是无差异的,注意是内容差异 无序性:集合内的顺序无关 (二)集合接口HashSet, ...

  4. 转---变量LEGB规则

    Python 变量作用域的规则是 LEGB LEGB含义解释: L -- Local(function):函数内的名字空间 E -- Enclosing function locals:外部嵌套函数的 ...

  5. 归并排序_JAVA

    import java.util.Arrays; public class Main { public static void main(String[] args) { int[] a = { 6, ...

  6. 02-Unity深入浅出(二)

    一. Unity声明周期 Unity容器为我们提供了6种生命周期,便于我们根据项目需求来选择使用. (1). 瞬时.默认省略即为瞬时,无论单线程还是多线程,每次都重新创建对象.new Transien ...

  7. 外部程序调用Django模块的解决办法

    Question django.core.exceptions.ImproperlyConfigured: Requested setting CACHES, but settings are not ...

  8. [转载]SpringMVC解决跨域问题

    本文转载自  https://www.cnblogs.com/morethink/p/6525216.html SpringMVC解决跨域问题, 感谢作者! 有个朋友在写扇贝插件的时候遇到了跨域问题. ...

  9. vue 高德地图使用 vue-amap

    此插件文档及使用还不错 https://elemefe.github.io/vue-amap/ <template> <div class="amap-page-conta ...

  10. JS处理数据四舍五入

    一,使用Math.toFixed toFixed() 方法可把 Number 四舍五入为指定小数位数的数字. 语法NumberObject.toFixed(num) 但是网友说toFixed bug比 ...