Debounce 和 Throttle 的原理及实现

当我们在处理诸如 resize 、 scroll 、 mousemove 和 keydown/keyup/keypress 等事件的时候,通常我们不希望这些事件太过频繁地触发,尤其是监听程序中涉及到大量的计算或者有非常耗费资源的操作。如果鼠标连续移动,那么浏览器就应该触发多个连续的 mousemove 事件,这意味着浏览器会在其内部计时器允许的情况下,根据用户移动鼠标的速度来触发 mousemove 事件。

Debounce

DOM 事件里的 debounce 概念其实是从机械开关和继电器的“去弹跳”(debounce) 衍生出来的,基本思路就是把多个信号合并为一个信号。

在 JavaScript 中, debounce 函数所做的事情就是, 强制一个函数在某个连续时间段内只执行一次,哪怕它本来会被调用多次 。我们希望在用户停止某个操作一段时间之后才执行相应的监听函数,而不是在用户操作的过程当中,浏览器触发多少次事件,就执行多少次监听函数。比如,在某个 3s 的时间段内连续地移动了鼠标,浏览器可能会触发几十(甚至几百)个 mousemove 事件,不使用 debounce 的话,监听函数就要执行这么多次;如果对监听函数使用 100ms 的“去弹跳”,那么浏览器只会执行一次这个监听函数,而且是在第 3.1s 的时候执行的。

现在,我们就来实现一个 debounce 函数。

/**
* @param fn {Function}   实际要执行的函数
* @param delay {Number}  延迟时间,也就是阈值,单位是毫秒(ms)
* @return {Function}     返回一个“去弹跳”了的函数
*/
function debounce(fn, delay) {
  var timer
  // 返回一个函数,这个函数会在一个时间区间结束后的 delay 毫秒时执行 fn 函数
  return function () {
    // 保存函数调用时的上下文和参数,传递给 fn
    var context = this
    var args = arguments
    // 每次这个返回的函数被调用,就清除定时器,以保证不执行 fn
    clearTimeout(timer)
    // 当返回的函数被最后一次调用后(也就是用户停止了某个连续的操作),
    // 再过 delay 毫秒就执行 fn
    timer = setTimeout(function () {
      fn.apply(context, args)
    }, delay)
  }
}

其实思路很简单, debounce 返回了一个闭包,这个闭包依然会被连续频繁地调用,但是在闭包内部,却限制了原始函数 fn 的执行,强制 fn 只在连续操作停止后只执行一次。
debounce 的使用方式如下:

$(document).on(‘mouvemove‘, debounce(function(e) {

}, 200))

我们在处理防止用户连续快速点击按钮时就可以采用这种方法解决。

Throttle

throttle 的概念理解起来更容易,就是 固定函数执行的速率 ,即所谓的“节流”。正常情况下, mousemove 的监听函数可能会每 20ms(假设)执行一次,如果设置 200ms 的“节流”,那么它就会 每 200ms 执行一次。比如在 1s 的时间段内,正常的监听函数可能会执行 50(1000/20) 次,“节流” 200ms 后则会执行 5(1000/200) 次。

/**
*
* @param fn {Function}   实际要执行的函数
* @param delay {Number}  执行间隔,单位是毫秒(ms)
* @return {Function}     返回一个“节流”函数
*/

function throttle(fn, threshhold) {
  // 记录上次执行的时间
  var last
  var timer

  // 默认间隔为 250ms
  threshhold || (threshhold = 250)

  // 返回的函数,每过 threshhold 毫秒就执行一次 fn 函数
  return function () {
    // 保存函数调用时的上下文和参数,传递给 fn
    var context = this
    var args = arguments
    var now = +new Date()
    // 如果距离上次执行 fn 函数的时间小于 threshhold,那么就放弃
    // 执行 fn,并重新计时
    if (last && now < last + threshhold) {
      clearTimeout(timer)

      // 保证在当前时间区间结束后,再执行一次 fn
      timer = setTimeout(function () {
        last = now
        fn.apply(context, args)
      }, threshhold)

    // 在时间区间的最开始和到达指定间隔的时候执行一次 fn
    } else {
      last = now
      fn.apply(context, args)
    }
  }
}

原理也不复杂,相比 debounce ,无非是多了一个时间间隔的判断,其他的逻辑基本一致。 throttle 的使用方式和debounce一样。

总结

debounce 强制函数在某段时间内只执行一次, throttle 强制函数以固定的速率执行。在处理一些高频率触发的 DOM 事件的时候,它们都能极大提高用户体验。

时间: 2024-08-24 20:56:36

Debounce 和 Throttle 的原理及实现的相关文章

debounce 与 throttle 区别

原文地址:http://undefinedblog.com/debounce-and-throttle/ 二.什么是debounce    1. 定义 如果用手指一直按住一个弹簧,它将不会弹起直到你松手为止. 也就是说当调用动作n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间. 三.什么是throttle      1. 定义 如果将水龙头拧紧直到水是以水滴的形式流出,那你会发现每隔一段时间,就会有一滴水流出. 也就是会说预先设定一个执行周期,当调用动作的时刻大于等于执行

JS debounce和throttle 去抖和节流

JS中的高频事件有scroll recize mouseover. 如果频繁触发事件绑定函数,会导致浏览器性能上的损失. 因此我们通常会添加延迟执行的逻辑. 在underscore.js里面有debounce和throttle这两个方法. throttle创建并返回一个节流阀一样的函数,当重复调用函数的时候,至少每隔wait毫秒调用一次该函数. var throttled = _.throttle(updatePosition, 100); $(window).scroll(throttled)

debounce与throttle区别

debounce(func, wait, immediate):创建并返回函数的防反跳版本,将延迟函数的执行(真正的执行)在函数最后一次调用时刻的wait毫秒之后,对于必须在一些输入(多是一些用户操作)停止之后再执行的行为有帮助.将一个连续的调用归为一个! throttle(func, wait, options):创建并返回一个像节流阀一样的函数,当重复调用函数的时候,最多每隔指定的wait毫秒调用一次该函数: 不允许方法在每wait ms间执行超过一次! debounce使用场景: 第一次触

关于debounce和throttle的区别及应用场景

电梯超时 想象每天上班大厦底下的电梯.把电梯完成一次运送,类比为一次函数的执行和响应.假设电梯有两种运行策略 `throttle` 和 `debounce` ,超时设定为15秒,不考虑容量限制. throttle 策略的电梯.保证如果电梯第一个人进来后,15秒后准时运送一次,不等待.如果没有人,则待机. debounce 策略的电梯.如果电梯里有人进来,等待15秒.如果又人进来,15秒等待重新计时,直到15秒超时,开始运送. 那什么时候该用 debounce 什么时候该用 throttle 呢?

debounce,throttle 区别及实现

debounce: 某段时间内,无论触发多少次,都在最后触发的一次后t秒时间去执行一次回调,eg:公交车不论上多少人,都会在最后一个人上车t秒后去执行关门的操作: throttle:每隔t秒去执行一次回调,eg:控制水龙头的阀门,每隔t秒掉下一滴水: 结合下面的图可以理解的更清楚些: 原文地址:https://www.cnblogs.com/fewhj/p/11755228.html

浅谈 Underscore.js 中 _.throttle 和 _.debounce 的差异

Underscore.js是一个很精干的库,压缩后只有5.2KB.它提供了几十种函数式编程的方法,弥补了标准库的不足,大大方便了JavaScript的编程. 本文仅探讨Underscore.js的两个函数方法 _.throttle 和 _.debounce 的原理.效果和用途. 通常的函数(或方法)调用过程分为三个部分:请求.执行和响应.(文中“请求”与“调用”同义,“响应”与“返回”同义,为了更好的表述,刻意采用请求和响应的说法.) 某些场景下,比如响应鼠标移动或者窗口大小调整的事件,触发频率

javascript中的throttle和debounce

throttle 我们这里说的throttle就是函数节流的意思.再说的通俗一点就是函数调用的频度控制器,是连续执行时间间隔控制.主要应用的场景比如: 1.鼠标移动,mousemove 事件2.DOM 元素动态定位,window对象的resize和scroll 事件 有人形象的把上面说的事件形象的比喻成机关枪的扫射,throttle就是机关枪的扳机,你不放扳机,它就一直扫射.我们开发时用的上面这些事件也是一样,你不松开鼠标,它的事件就一直触发.例如: 复制代码 代码如下: var resizeT

性能提速:debounce(防抖)、throttle(节流/限频)

debounce与throttle是用户交互处理中常用到的性能提速方案,debounce用来实现防抖动,throttle用来实现节流(限频).那么这两个方法到底是什么(what)?为何要用(why-解决什么问题)?具体的实现原理,以及函数运行过程是怎样的呢(how)? 1.what? 连续操作:两个操作之间的时间间隔小于设定的阀值,这样子的一连串操作视为连续操作. debounce(防抖):一个连续操作中的处理,只触发一次,从而实现防抖动. throttle:一个连续操作中的处理,按照阀值时间间

JS魔法堂:函数节流(throttle)与函数去抖(debounce)

一.前言 以下场景往往由于事件频繁被触发,因而频繁执行DOM操作.资源加载等重行为,导致UI停顿甚至浏览器崩溃. 1. window对象的resize.scroll事件 2. 拖拽时的mousemove事件 3. 射击游戏中的mousedown.keydown事件 4. 文字输入.自动完成的keyup事件 实际上对于window的resize事件,实际需求大多为停止改变大小n毫秒后执行后续处理:而其他事件大多的需求是以一定的频率执行后续处理.针对这两种需求就出现了debounce和throttl