前几天我将这两个概念搞混了,然后有人纠正了我。因此,我将其添加到我的博客文章想法列表中,现在我们就在这里。它们都是基于 DOM 事件限制 JavaScript 执行次数以提高性能的方法。但正如你所猜想的,它们是不同的。
节流强制限制函数在一段时间内被调用的最大次数。例如,“每 100 毫秒最多执行此函数一次”。
假设在正常情况下,您将在 10 秒内调用此函数 1000 次。如果您将其节流为每 100 毫秒调用一次,则该函数最多只会执行 100 次。
(10 秒 * 1000) = 10000 毫秒
10000 毫秒 / 100 毫秒节流 = 100 次最大调用
去抖强制确保在一段时间内函数不会再次被调用。例如,“仅当 100 毫秒内未调用此函数时才执行此函数”。
也许某个函数在 3 秒内以短促的爆发方式被调用了 1000 次,然后停止被调用。如果您将其去抖设置为 100 毫秒,则该函数只会在 3.1 秒时触发一次,即爆发结束后。在爆发期间每次调用该函数都会重置去抖计时器。
有什么意义?
这些概念的一个主要用例是某些 DOM 事件,例如滚动和调整大小。例如,如果您将滚动处理程序附加到某个元素,然后向下滚动该元素 5000 像素,您可能会看到触发了 100 多个事件。如果您的事件处理程序执行大量工作(例如繁重的计算和其他 DOM 操作),您可能会遇到性能问题(卡顿)。如果您可以在很大程度上减少处理程序的执行次数,而不会对体验造成太大影响,那么这样做可能值得。
快速示例
- 等待用户停止调整窗口大小
- 在用户停止输入之前不要触发 ajax 事件
- 测量页面的滚动位置,并最多每 50 毫秒响应一次
- 在应用程序中拖动元素时确保良好的性能
怎么做
这两个函数都内置于 Underscore 和 Lodash 中。即使您不完全使用这些库,您也可以随时从其中提取函数以供自己使用。
简单的节流滚动
$("body").on('scroll', _.throttle(function() {
// Do expensive things
}, 100));
简单的去抖调整大小
$(window).on('resize', _.debounce(function() {
// Do expensive things
}, 100));
从这里升级,您将使用 requestAnimationFrame,因此即使执行了函数,浏览器也会根据其自己的理想时机进行执行。这在 Paul Lewis 的本教程中有所介绍。
演示
简单的演示,以便您可以体验两者之间的差异
查看 Chris Coyier 在 CodePen 上的笔 节流、去抖和两者都不使用之间的区别(@chriscoyier)。
更新: 更多演示!
我认为基于时间的去抖只是几种有效方法中的一种,您还可以严格根据未完成的行为进行去抖。
考虑一个启动“添加到购物车”行为的按钮。您希望确保即使用户意外双击,也只会将一个商品添加到购物车。您可以根据时间进行去抖(超过 500 毫秒的点击是两次点击,而不是一次双击),或者您可以使用一个标志来保护函数,该标志会阻止任何进一步的点击,直到该过程完成后清除该标志。
请不要假设我对常用 UI 元素的交互做错了。我的汽车收音机去抖了,所以如果我试图太快地更改电台,我将无处可去,这让我想要把这东西从我的仪表盘上扯下来。
这不是一个很好的例子,因为您已经可以访问双击事件。只需阻止双击操作即可。
除了 Chris 所说的之外,使用固定的时间量来区分点击和双击是不好的,因为操作系统可能会允许用户配置构成双击的时间范围 - 因此您 500 毫秒的假设可能根本就是错误的。
所有观点都有效,我最初的想法很简单,去抖不像这里定义的那样简单明了。是的,可能存在更好的例子,是的,人们绝对应该小心使用它。
但是,理解去抖模式,而不仅仅是时间相关的去抖,是一个很好的工具,可以放在你的口袋里。
有没有什么特别的原因导致它还没有标准化?
我认为 Underscore 非常标准,我肯定已经开始在所有地方使用它了;)
Lodash 算是新的 Underscore,我建议您查看一下。它有一些额外的实用功能,最初是作为 Underscore 的分支创建的。
别老说“Lodash 是新的 Underscore”
Underscore 53k
Lodash 407k
Lodash 可能更适合 Node.js
节流: 步进、捕捉、网格。例如:自定义范围滑块上的持久值。
去抖: 等待空闲。例如:在文本字段中键入后触发 AJAX 搜索结果,下拉菜单中的悬停状态动画技巧→ 除非用户停止在父菜单上移动鼠标指针,否则不要显示下拉菜单。
关于您的节流示例,需要注意的是:您将在相关的 10 秒内最多获得 100 次调用。
但是,至少使用
underscore
的方法,其他 900 次调用将在接下来的 90 秒内进行。lodash
为返回的函数添加了一个cancel
方法,如果您愿意,可以使用该方法放弃任何当前延迟的调用。我敢肯定那是不对的。在他提供的示例中肯定不是这样。
Jeremy T,你说得完全正确。
我不确定我当时在想什么。