资讯专栏INFORMATION COLUMN

JavaScript Debounce&Throttle

The question / 1611人阅读

摘要:如果我们的回调函数较为复杂,页面的性能就会变差。而可以保证稳定的时间间隔执行一次回调函数。但需要弄清楚的是,无论是还是,控制的都是回调函数的执行,而不是事件的监听。

前言

假设现在有个需求:监听滑动事件,并执行回调。
当你用触摸板或者鼠标滑动页面时,每秒钟大概会触发几十次scroll事件,而当你在手机
等移动终端上滑动页面时,每秒就会触发一百次scroll事件。如果我们的回调函数较为复杂,页面的性能就会变差。

解决问题的两种工具:debounce、throttle,它们有些类似,比如作用都是控制目标函数在一段时间内执行的次数;但更多的是不同:debounce使得在前后两次事件间隔不超过一定时间的情况下,无论触发多少次事件都只会执行一次回调函数。而throttle可以保证稳定的时间间隔执行一次回调函数。但需要弄清楚的是,无论是debounce还是throttle,控制的都是回调函数的执行,而不是事件的监听

另外,debounce和throttle都只是一种思想,可以有很多种实现,当然也可以自己去实现,后文中的代码都是基于lodash中的debounce和throttle。

debounce

想象这样一个场景:电梯即将关门,这时有个人上电梯,电梯就会停止关门。过了一会儿(间隔在电梯完全关上门所需要的时间之内),电梯又准备关门,又有人上电梯,又重复之前的步骤,直到最后一个人进来,电梯完全关上门,整个过程中电梯只关了一次门。

这个场景可以说是debounce在现实生活中的一个模型。回到代码层面:

// debounce(callback, millisecond, options)
var onScroll = debounce(animation, 1000, { leading: true, trailing: false });

// right
$("#container").addEventListener("scroll", onScroll);

// wrong
$("#container").addEventListener("scroll", function(){
    debounce(callback, millisecond, options);
});

debounce接受三个参数:要控制的函数、两次事件间隔的最大毫秒数、以及配置对象,返回一个函数,通常直接作为事件处理函数。详细说说第三个参数options,此参数默认值为:

{ leading: false,  trailing: true }

leading: 事件一被触发,先执行一次回调函数,再对之后的调用做控制。这样做的好处是事件一被触发,回调就执行,更真实;
trailing: 先对回调函数做控制,直到事件触发间隔超过设定时间,再调用回调函数,像上面电梯关门的例子
两者同时为true时,一次控制过程中回调会被执行两次;两者同时为false时,回调不执行。

常见的应用场景:拖拽窗口的大小、实时验证input

throttle

相比于debounce,throttle更像是一个特殊化的setInterval,就是说throttle包装过的函数会按固定的时间间隔执行,区别在于这个执行跟事件的触发有关,并且不用像setInterval那样手动取消。

throttle(callback, millisecond)

所以throttle更适用于需要不断执行但又需要控制执行次数来优化性能的函数,比如在滑动时根据滑动的数据(scrollTop等)不断改变某元素的样式。这种情况下,间隔时间设的过长就会不流畅,过短又起不到优化的效果。一般设为16ms,这样可以让帧率达到60fps,保持良好的视觉效果。说到这里就不得不提浏览器原生API requestAnimationFrame了。

粗略地说,requestAnimationFrame(callback)相当于

throttle(callback, 16);

rAF的优点在于它是原生的API,较为稳定。当然也有不少缺点,比如需要手动的启动和取消;浏览器tab不是active的时候不会被执行;不支持IE9;

caveat

虽然说debounce、throttle有很多实现,甚至可以自己实现,但还是推荐直接使用loadash或者underscore,专业的工具库考虑到的事情往往比我们自己更多,不用担心为了使用两个函数而把整个lodash库都引入的问题,lodash包是可定制的,具体的方法自行Google。

尽量将debounce或者throttle生成的函数直接作为事件处理函数,避免写出这种错误的代码:

$("#container").addEventListener("scroll", function(){
    // 这里只是生成了函数,并没有执行,即使执行也无法达到控制的效果
   debounce(callback, millisecond, options);
});

使用变量保存debounce或者throttle返回的值后,可以调用取消的方法,就像setTimeout那样:

onScroll = debounce(animation, 1000, { leading: true,             
trailing: false });

$("#container").addEventListener("scroll", onScroll);

onScroll.cancel();

参考文章: Debouncing and Throttling Explained Through Examples

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/91779.html

相关文章

  • JavaScript Throttle & Debounce

    摘要:无视一定时间内所有的调用,适合在发生频度比较高的,处理比较重的时候使用。一定间隔内没有调用时,才开始执行被调用方法。 Throttle 无视一定时间内所有的调用,适合在发生频度比较高的,处理比较重的时候使用。 var throttle = function (func, threshold, alt) { var last = Date.now(); threshold...

    jone5679 评论0 收藏0
  • The debounce & throttle in Javascript(防抖与节流)

    摘要:您的支持是我最大的动力,我会保证提供高质与清晰的文章与您共同成长。一些文章中的与上面所谈到的设置类似。防抖防抖技术允许我们捆绑多个连续调用成为单一的一次调用。防抖的应用这个简单的举个 欢迎star和watch我的github issue blog,欢迎加入讨论。您的支持是我最大的动力,我会保证提供高质与清晰的文章与您共同成长。 节流[throttle]与防抖[debounce]在前...

    XboxYan 评论0 收藏0
  • 快速 TypeScript 化 lodash 中的 throttle & debounce

    摘要:背景需要包写起来爽,然而如果遇到没有现成的化的工具函数,就需要自己想办法弄出一份类型声明文件了。最为重要的是,这种迁移方面我们可以随意自定义化中所需要的工具函数,迁移粒度都可以由自己控制。 1、背景 1.1、需要 TS 包 TypeScript 写起来爽,然而如果遇到没有现成的 TS 化的工具函数,就需要自己想办法弄出一份类型声明文件了。 前两天要写的小工具库(Typescript 语...

    lewinlee 评论0 收藏0
  • 【源码分析】给你几个闹钟,或许用 10 分钟就能写出 lodash 中的 debounce &amp

    摘要:最简单的案例以最简单的情景为例在某一时刻点只调用一次函数,那么将在时间后才会真正触发函数。后续我们会逐渐增加黑色闹钟出现的复杂度,不断去分析红色闹钟的位置。 序 相比网上教程中的 debounce 函数,lodash 中的 debounce 功能更为强大,相应的理解起来更为复杂; 解读源码一般都是直接拿官方源码来解读,不过这次我们采用另外的方式:从最简单的场景开始写代码,然后慢慢往源码...

    余学文 评论0 收藏0
  • js中函数节流&函数去抖

    摘要:节流保证在一定时间内,只能触发一次。我们在尝试一下去抖消抖,消除抖动,感觉这个更好听有没有什么现成的上的一次发现源码的经历以及对学术界拿来主义的思考函数节流和函数去抖应用场景辨析函数去抖的实现 开篇先提几个问题? 1.做搜索框的时候你使用什么事件?change?blur?keyup?你想要的效果是什么? 2.scroll事件怎么就触发?是滚一段距离触发一次?还是滚一圈触发一次?还是滚...

    王军 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<