前几天看到一篇文章,我的公众号里也分享了《一次发现underscore源码bug的经历以及对学术界拿来主义的思考》具体文章详见,微信公众号:

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

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

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

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

<p data-height="268" data-theme-id="0" data-slug-hash="xVpoOe" data-default-tab="result" data-user="ghostcode" class="codepen">See the Pen Scroll events counter by ghostcode (@ghostcode) on CodePen.</p>

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

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

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

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

Debounce

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

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

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

<p data-height="268" data-theme-id="0" data-slug-hash="vGpqLO" data-default-tab="result" data-user="ghostcode" class="codepen">See the Pen Debounce. Trailing by ghostcode (@ghostcode) on CodePen.</p>

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

Leading edge (or "immediate")

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

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

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

自己尝试一下:

<p data-height="268" data-theme-id="0" data-slug-hash="VaQwRm" data-default-tab="result" data-user="ghostcode" class="codepen">See the Pen Debounce. Leading by ghostcode (@ghostcode) on CodePen.</p>

Debounce Implementations

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

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

<p data-height="268" data-theme-id="0" data-slug-hash="GZQRLv" data-default-tab="result" data-user="ghostcode" class="codepen">See the Pen debounce-click by ghostcode (@ghostcode) on CodePen.</p>

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

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

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

Debounce Examples

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

<p data-height="268" data-theme-id="0" data-slug-hash="PNQorE" data-default-tab="result" data-user="ghostcode" class="codepen">See the Pen Debounce Resize Event Example by ghostcode (@ghostcode) on CodePen.</p>

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

AutoComplete中的Ajax请求使用的keypress

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

<p data-height="268" data-theme-id="0" data-slug-hash="wGyvVj" data-default-tab="result" data-user="ghostcode" class="codepen">See the Pen Debouncing keystrokes Example by ghostcode (@ghostcode) on CodePen.</p>

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

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

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

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

一个常见的陷阱:

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

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

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

Throttle

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

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

Throttling Examples

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

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

<p data-height="268" data-theme-id="0" data-slug-hash="xVYbGZ" data-default-tab="result" data-user="ghostcode" class="codepen">See the Pen Infinite scrolling throttled by ghostcode (@ghostcode) on CodePen.</p>

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在复杂场景下或许会提供更好的结果。

<p data-height="268" data-theme-id="0" data-slug-hash="qZxEaq" data-default-tab="result" data-user="ghostcode" class="codepen">See the Pen Scroll comparison requestAnimationFrame vs throttle by ghostcode (@ghostcode) on CodePen.</p>

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

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

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

throttle与debounce的区别相关推荐

  1. debounce实现 js_Vue.js以组件或者插件的形式实现throttle或者debounce

    Vue.js以组件或者插件的形式实现throttle或者debounce 发布于 2020-3-9| 复制链接 摘记: 需求在业务中,会碰到许多点击请求的情况,在请求前改变一个lock变量(在lock ...

  2. 节流(Throttle)与防抖(Debounce)区别与demo实现+ 图解

    一.节流(Throttle) 无论节流还是防抖,我们都会在事件的处理函数中去统计时间判断,那函数内部有,每一次执行函数,都会对内部变量进行一个判断,这个变量在函数执行之后,不应该被销毁,所以这里用到闭 ...

  3. js实现 throttle 和 debounce

    1.throttle 节流:drag改变浏览器大小,触发onresize函数,实现拖动每过1秒输出一次,不足1秒,1秒后输出一次.多用于高频操作,如抢票.抢购等,无论点击多少次,只固定间隔执行一次,以 ...

  4. JavaScript中的定时控制-Throttle、Debounce、Immediate的基本概念

    Throttle 在现代浏览器中,帧速率为60fps是流畅性能的目标,给定我们16.7ms的时间预算用于响应一些事件所有需要的更新.这样可以推断,如果每秒发生n个事件并且回调执行,需要t秒的时间,为了 ...

  5. java实现debounce_利用throttle和debounce实现延迟请求

    当你在前端点击一个搜索往后面发送请求的时候会触发一个像后端的请求,当你不在前端做一些控制的时候,就会发现会发送特别多的请求 比如一个人员搜索,虽然前端控制了当搜索字符大于3个字的时候才发送,但是后续还 ...

  6. 函数节流(Throttle)和防抖(Debounce)解析及其OC实现

    Python实战社群 Java实战社群 长按识别下方二维码,按需求添加 扫码关注添加客服 进Python社群▲ 扫码关注添加客服 进Java社群▲ 作者丨highway 来源丨iOS成长之路 Thro ...

  7. 一次发现underscore源码bug的经历以及对学术界『拿来主义』的思考

    事情是如何发生的 最近干了件事情,发现了 underscore 源码的一个 bug.这件事本身并没有什么可说的,但是过程值得我们深思,记录如下,各位看官仁者见仁智者见智. 平时有浏览园区首页文章的习惯 ...

  8. 为什么说百度AMIS框架是一个优秀的设计

    AMIS是百度开源的一个前端低代码框架,公允的说,它是目前前端开源社区中设计最精良的低代码框架之一,代表了国内前端开源低代码领域的最高水平.AMIS采用JSON语法格式,内置了大量开箱即用的成熟组件, ...

  9. debounce与throttle区别

    在2011年,Twitter网站曾爆出一个问题:在主页往下滚动时,页面会变得缓慢以致没有响应.John Resig发表了一篇文章< a blog post about the problem&g ...

最新文章

  1. UIImage 各种处理(分类)
  2. html css float left与 float right的使用说明
  3. java lr分析表建立程序_[源码和文档分享]基于Java实现的LR(1)分析法语法分析程序...
  4. 12- APP接口测试以及接口文档的分析
  5. 全局对象_C++全局变量初始化
  6. 数字图像处理图像反转的实现_反转8位数字| 8085微处理器
  7. Linux操作系统原理与应用07:内核同步
  8. 大数据可视化有什么优点
  9. 科研_今天,我们怎么做科研?
  10. 重庆科技学院计算机专业好吗,重庆科技学院什么专业好
  11. 渗透测试 2 --- XSS、CSRF、文件上传、文件包含、反序列化漏洞
  12. 鸿蒙不是手机系统?智慧屏曝光,华为将布局“贾维斯”智能时代
  13. 机器视觉——光源选型
  14. VMware 安装心得
  15. 做祛痘产品微信是如何引流的?祛斑祛痘产品引流加粉渠道有哪些?
  16. Android中的RAM、ROM、SD卡以及各种内存的区别
  17. C语言程序设计ncre,NCRE二级C语言程序设计辅导
  18. android 工具 Draw 9-patch 详解
  19. Elasticsearch集群原理、安装和基本使用
  20. 对梯度幅值进行非极大值抑制

热门文章

  1. Android TextView
  2. [K/3Cloud]关于数据库sa密码更改,管理中心登录不上的问题。
  3. Spring从菜鸟到高手(四)(上)使用JdbcTemplate类实现用户登陆验证、批量更新
  4. c语言标准库低通的qsort函数不适宜所有排序任务的原因
  5. IdentityServer4关于多客户端和API的最佳实践【含多类型客户端和API资源,以及客户端分组实践】【中】...
  6. electron打包可选择安装位置,可自动更新
  7. 软件开发文档模板 (学习)
  8. 如何重构“箭头型”代码
  9. vue从入门到进阶:指令与事件(二)
  10. 5.JasperReports学习笔记5-其它数据生成动态的报表(WEB)