debounce 与 throttle 区别

原文地址:http://undefinedblog.com/debounce-and-throttle/

二、什么是debounce                            

   1. 定义

  如果用手指一直按住一个弹簧,它将不会弹起直到你松手为止。

      也就是说当调用动作n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间。

三、什么是throttle                              

   1. 定义

  如果将水龙头拧紧直到水是以水滴的形式流出,那你会发现每隔一段时间,就会有一滴水流出。

  也就是会说预先设定一个执行周期,当调用动作的时刻大于等于执行周期则执行该动作,然后进入下一个新周期。

 

在实现一些需要被频繁调用的函数时,我们通常都会使用 debounce 或 throttle方法。在我的印象中,它们的作用就是减少函数被调用的次数,但具体有什么区别,却真的不能说清楚。

直到最近看了一篇精彩的博文,用可视化的方法展示了两者的区别,很有启发性,值得参考。

注意到上图,第一行 Mousemove Events 展示了 mousemove 事件触发的频率。第二行和第三行是分别使用 underscore 与 jQuery 的 debounce 方法后事件的触发频率。第四、五行则是增加了一个 delay 参数后的触发频率。与之对比的是最后三行,使用的是 throttle 方法。

首先我们的直观感觉是使用 debounce 方法相比于 throttle 方法事件触发的频率更低,但实际上不能这么理解。要解释上图的结果,首先需要了解 debounce 和 throttle 的原理。

当我们阅读 lodash 的代码时会发现,throttle 方法不过是 debounce 方法的一个修饰。也就是说,_.throttle 和 _.debounce 最终都会都会调用 debounce 方法。那么 debounce 究竟是如何工作的呢?

首先看 _.debounce 的 API:

_.debounce(func, delay, options);  

func 是需要被调用的目标函数,delay 是时间限制,options 是一系列的配置,为了简化问题,这里不多提。

当调用 _.debounce 后,会返回一个函数,这个函数在被调用时会生成一个 setTimeout(delayed, delay)。其中 delayed 又是一个内部方法,在 delayed 被调用时进行如下检测:当前时间 - 上次func被调用事件 是否 小于 0 或 大于 delay ?如果是则执行一次 func,记录并返回执行结果,同时更新上次被调用时间;如果不是则调用 setTimeout 进行下一次的判断。

因此,当你像下面这样绑定事件,

$(window).on('mousemove', _.debounce(moveHandler, 500));

并频繁移动鼠标时,你会发现 moveHandler 压根没有被调用过!直到你停下来后,moveHandler 才会被调用一次。

至于 _.throttle 方法,只不过是多给 debounce 传了一个 maxWait 选项,这个选项的意思是至少保证在每 maxWait 时间让 func 被调用一次。

说到这儿,你应该就明白了为什么会出现上图那样的调用情况。如果你频繁的移动鼠标,throttle 会保证在每 maxWait 时间调用 func 一次,而 debounce 如果没有明确设置 maxWait,是一直不会调用 func 直到你停止移动鼠标后才会调用一次。

 

因此,可以笼统的说 _.throttle 就是设置了 leading=true, trailing=true 及 maxWait 的 _.debounce。

那什么时候该用 debounce 什么时候该用 throttle 呢?

下面我列举了一些场景:

  • input 中输入文字自动发送 ajax 请求进行自动补全: debounce
  • resize window 重新计算样式或布局:debounce
  • scroll 时更新样式,如随动效果:throttle

最重要的还是理解两者对调用时间及次数上的处理,根据业务逻辑选择最合适的优化方案!

posted @ 2016-06-12 17:01  ICupid  阅读(989)  评论(0编辑  收藏  举报