专栏首页前端黑板报throttle与debounce的区别

throttle与debounce的区别

注:文章中有例子无法在微信里面展示,最好看原文。

以前写过一篇文章《“节流函数”提高性能》,里面讲到用函数“节流”来减少执行次数(不影响体验的情况下),其实实现的代码并没有问题,但是第二个方法的函数名有问题。前几天看到一篇文章,我的公众号里也分享了《一次发现underscore源码bug的经历以及对学术界拿来主义的思考》具体文章详见。

文中讲了大家对throttle和debounce存在误解,同时提到了《高程3》中实现节流方法存在一些问题,为了更好的理解这两个概念,搜了很多相关文章,详见文章底部。

throttle与debounce是两个类似的概念,目的都是随着时间的推移控制执行函数的次数,但是有些细微的差别。

当我们为DOM事件关联方法时,若我们有一个debounced和throttled函数将会很方便,为何?因为这样我们可以在事件和执行函数之间添加一层控制,注意我们并没有去控制DOM事件触发的次数。

例如,我们谈一下scroll事件,看下面的例子:

当你在触控板或者鼠标滚动时,每次最少会达到30次,在手机上更多。可是你的滚动事件处理函数对这个频率是否应付的过来?

在2011年,Twitter网站曾爆出一个问题:当你在主页往下滚动时,页面会变得缓慢以致没有响应。John Resig发表了一篇文章《 a blog post about the problem》指出直接在scroll事件上面绑定高消耗的事件是一个多么愚蠢的想法。

在那个时候John建议使用一个独立于scroll事件且每250ms执行的轮询方法。这样的话处理方法就不会耦合于事件。通过这个简单的技术,我们可以提高用户体验。

现在有一些更先进的事件处理方法,让我来给你介绍:Debounce,Throttle和requestAnimationFrame,同时会介绍一些适用的场景。

Debounce

Debounce技术使我们可以将一个连续的调用归为一个。

想象你在电梯的场景,当电梯门开始要关闭的时候,突然一个人进来,此时电梯并不会关闭并且也不会执行改变楼层的方法,如果还有人进来同样的事情会发生:电梯延迟执行它的方法(改变楼层),优化了它的资源。

自己尝试一下,在按钮上点击或者移动鼠标:

你可以看到快速连续的事件是如何通过一个debounce事件来表示的。

Leading edge (or “immediate”)

你可以发现事件结束的时候,debounce的事件并没有立即执行而是等待了一些时间才触发。为何不立即触发,就像开始没有使用debounce事件处理?直到在连续执行的事件中有一个暂停,才会再次触发。

你可以通过一个leading的参数做到:

在underscore.js中,这个参数叫immediate。

自己尝试一下:

Debounce Implementations

2009年在John Hann的文章中第一次看到debounce的实现方法。

在那之后不久,Ben Alman写了一个jQuery插件(现在不在维护),一年以后Jeremy Ashkenas把此方法添加到underscore.js中,不久又被添加到lodash中。

这三种实现方法内部不同,但是接口几乎一致。

有段时间underscore采用了Lodash的实现方法,但是在我发现了一个bug之后,自此两个库的实现开始分道扬镳。

Lodash在.debounce和.throttle中添加了许多特性。immediate标示替代了leading和trailing。你可以二选一或者都选,默认情况下,只有trailing是开启的。

Debounce Examples

当改变浏览器窗口时,resize事件会触发多次。

如你所见,我们使用了trailing参数,因为我们只对用户停止改变浏览器大小时最后一次事件感兴趣。

AutoComplete中的Ajax请求使用的keypress

当用户仍旧在输入的时候,为何每隔50ms发送Ajax请求? _.debounce 可以帮助我们避免额外的工作,只在用户停止输入的时候发送请求。

另一个使用场景是在进行input校验的时候,“你的密码太短”等类似的信息。

如何使用debounce和throttle以及常见的陷阱?

可以自己实现这两个方法或者随便复制别人blog中的实现方法,我的建议是直接使用underscore和lodash中的方法。如果你只需要这两个方法,可以定制输出lodash方法:

12

npm i -g lodash-clilodash-cli include=debounce,throttle

一个常见的陷阱:

1234567

// WRONG$(window).on('scroll', function() { _.debounce(doSomething, 300); });// RIGHT$(window).on('scroll', _.debounce(doSomething, 200));

debounce方法赋值给一个变量之后允许我们调用一个私有方法:debounced_version.cancel()

12345

var debounced_version = _.debounce(doSomething, 200);$(window).on('scroll', debounced_version);// If you need itdebounced_version.cancel();

Throttle

使用 _.throttle ,我们不允许方法在每Xms间执行超过一次。

和debounce的主要区别是throttle保证方法每Xms有规律的执行。

Throttling Examples

一个相当常见的例子,用户在你无限滚动的页面上向下拖动,你需要判断现在距离页面底部多少。如果用户快接近底部时,我们应该发送请求来加载更多内容到页面。

在此 _.debounce 没有用,因为它只会在用户停止滚动时触发,但我们需要用户快到达底部时去请求。通过_.throttle 我们可以不间断的监测距离底部多远。

requestAnimationFrame (rAF)

requestAnimationFrame是另一个频率限制的方法。

它可以通过 _.throttle(dosomething, 16)实现,但为了更加精准浏览器提供了内置API。

我们可以使用rAF API作为throttle方法的替代,考虑一下利弊:

利:

  • 目标60fps(16ms每贞),但是内部使用最优的时间间隔来渲染
  • 使用简单并且是标准API,以后不会变动,不需要维护

弊:

  • rAF的开始或者取消需要我们自己处理,不像.debounce和.throttle内部实现
  • 浏览器Tag没有激活,它就不会执行
  • 即使多数现代浏览器支持,但是IE9,Opera Mini以及老版本Android依旧不支持。A polyfill到现在依旧需要
  • rAF在node.js中不支持

根据经验,我建议在JS执行”painting”或”animating”中直接操作属性和重新计算元素位置时使用rAF。

发送Ajax请求或者是否添加/删除class(触发一个CSS动画)时,我会考虑debounce和throttle,此时你可以降低执行频率(200ms而不是16ms)。

rAF的例子

在Paul Lewis的文章激发下,我只在scroll事件中提供例子。

我一步步的调throttle到16ms,希望给一个类似的体验,但是rAF在复杂场景下或许会提供更好的结果。

一个更好的例子我是在headroom.js中看到的,这里通过一个对象封装,进行了逻辑解藕。

总结: 使用debounce,throttle和requestAnimationFrame优化你的事件处理函数。每一个方法有一些细微的差别,三个都很有用而且互相弥补。

  • debounce:把突然涌进的事件(键盘事件)归位一个
  • throttle:保证持续执行方法分隔为每Xms执行一次。就像每200ms监测滚动位置来触发css动画。
  • requestAnimationFrame:throttle的替代方案,当你的方法需要重新计算和渲染元素同时你需要更平滑的变动或动画。注意:IE9- 不支持。
  1. https://blog.coding.net/blog/the-difference-between-throttle-and-debounce-in-underscorejs
  2. https://css-tricks.com/the-difference-between-throttling-and-debouncing/
  3. http://stackoverflow.com/questions/25991367/difference-between-throttling-and-debouncing-a-function
  4. http://demo.nimius.net/debounce_throttle/

本文分享自微信公众号 - 前端黑板报(FeHeiBanBao),作者:zhuy

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2016-04-10

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 全栈工程师权威指南

    根据 Stack Overflow 2016年开发者调查显示,全栈工程师是最受欢迎的开发者职业。毫无疑问现在有许多在线或个人课程来帮助程序员成为全栈开发者,甚至...

    前端黑板报
  • 小程序-获取多个formId

    如同上面的几行代码,只要你点击登录按钮,就能提交一次表单,也能获取一次formId,但是想要实现点击一次提交多次,光靠复制这几行代码,貌似还不行,无论是缩小按钮...

    前端黑板报
  • Mac配置Maven

    Maven项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具。 1.下载安装Maven 安装其实就是解压到一个目录里面...

    前端黑板报
  • 唤醒词引擎对比分析

    唤醒词检测在语音用户界面(Voice User Interface)拥有广阔的应用,特别是其支持自然语音交互而无需双手。

    用户6026865
  • 脑机前沿 | 科学家使用多巴胺无缝连接人工神经元和生物神经元

    通过模仿生物系统的分布式信息处理,以大脑为灵感的计算范例已在视觉和语言任务的自动化方面取得了实质性进步。人工神经网络(ANN)与生物系统之间的相似性启发了人工神...

    脑机接口社区
  • 手机淘宝性能优化全记录

    手机淘宝作为一个航母级的应用,承载了100多个业务方,部分是H5的形式接入,还有超过50个Native的业务方。为了规避安卓DEX65535的方法数限制以及各业...

    IT苦逼一枚
  • 谷歌推出开源 Python 库“Tangent”,支持前向模式自动微分

    日前,Google Research Blog 推出开源 Python 库“Tangent”。据介绍,这个库与现有的机器学习库相比,存在诸多优势,可以大大改善了...

    AI研习社
  • 开发 | 谷歌推出开源 Python 库“Tangent”,支持前向模式自动微分

    AI科技评论消息:日前,Google Research Blog 推出开源 Python库“Tangent”。据介绍,这个库与现有的机器学习库相比,存在诸多优势...

    AI科技评论
  • MobileNetV1/V2/V3简述 | 轻量级网络

    论文: MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applic...

    VincentLee
  • [Linux]Linux下安装和配置solr/tomcat/IK分词器 详细实例一.

    一枝花算不算浪漫

扫码关注云+社区

领取腾讯云代金券