Debounce and Throttle 的可視化解釋
Debounce 和 Throttle是两个概念,我们可以在 JavaScript 中使用,以提高我们对执行功能的控制,在事件处理的时候特别有用。
比喻
这两种技术都回答同样的问题“一个功能可以随着时间被推移多久?”
- Debounce:把它看成是“多个分组事件之一”。试想一下,你回家,再进入电梯,门正在关闭……,突然你的邻居出现在大厅,并试图跳上电梯。要有礼貌!并打开大门,他说:你等等,让我一起上。相同的情况下,可以与第三人再次发生,等等…这可能会延缓出发几分钟。
- Throttle:把它看成是一个阀,它规定了执行的流程。我们可以判断函数可以在一定时间内被调用的最大次数。因此,再用电梯比喻..你有足够的礼貌,持续10秒等人,但一旦时间到了,你一定要走!
无 Debounce 或 Throttle 事件处理程序就像一次只能一个人用的电梯:没有那么高效。
我希望这个坏比喻对你有所帮助,但有时言语并不会对掌握这些概念有多大的帮助,所以我创建了一个演示去理解 Debounce 和 Throttle 概念,并把它们应用到mousemove事件。
function elevator_departure(name){
alert(name + " was the last one. Nobody else? Let's go then");
};
var debounced_elevator_departure = $.debounce(200, false, elevator_departure);
debounced_elevator_departure('John');
debounced_elevator_departure('Mike');
debounced_elevator_departure('Peter');
// You will see *only* one message, "Peter was the last one. Nobody else? Let's go then";
我发现这3个人在JavaScript中都通过debounce和throttle执行了。我真的建议你先读一下 Ben Alman's
的文章,以便更好地理解 debounce 和 throttle. underscore
和 lodash
有不同的实现方式,但参数都是一样的(除了 throttle
没有 trailing
).
- Underscore.js by Jeremy Ashkenas *
- Lodash.js by John-David Dalton *
- jQuery Plugin by Ben Alman.
演示
这里是视觉演示:
这是截图:
注意:
- 这个的源代码演示在GitHub上托管。
- 如果你看到动画在浏览器中并不流畅,请在一个单独的页面打开演示,或者尝试在Chrome中。
- 我通过在鼠标区域
tapping
来去在Android
环境下演示它 - 每个单元代表约30毫秒,但JS是单线程的,所以浏览器是不是准确。这个演示并不是想成为一个最佳实践,只是为了更好地去理解概念。
- 当你做了
setTimeout
,时间间隔可能不同,在每个浏览器在4ms和15ms的之间(尼古拉斯Zakas的文章),甚至是setTimeout(fn,0)
至少需要4毫秒。在另一方面,时间为1毫秒为最小单位的。 - 在
mousemove
第一行中的事件也有做节流(80毫秒)。这对理解演示有所帮助。 - 要获得
trailing
选项$.throttle
,你需要传递false
。 - 不要忘了,这一切都是builder,他们返回一个函数,所以你只需要执行一次。
应用场景
debounce 案例
用它减少执行频繁的事件。例子:
- 当将要处理的textarea的快速打字:你不想执行处理函数,直到用户停止输入再来处理文本。
- 当数据保存到通过AJAX服务器:你不想每秒调用非常缓慢的邮件服务器
throttle 案例
和debounce
一样,但如果想每隔一段时间必须执行事件,可以使用它:
- 假设用户不停地快速输入30秒,可能你会想每5秒执行一次函数;
- 一些对性能有影响的事件但又必须处理:scroll, mouseweel, mousemove。一个简单的鼠标滚轮运动可以在一秒钟内触发几十个事件。
本文翻译自 Debounce and Throttle: a visual explanation
========= update on 11-21 =========
example
debounce
var validation = _.debounce(function () {
... // 校验输入,逻辑可能还会有点复杂
}, 250)
jQuery('input').keyup(validation) // 输入完成或停顿250ms后才去执行校验,如果不停输入10s,则10s内都不会触发
/**
* debounce第三个参数为可选参数,
* leading为true时,则在事件开始时首先执行一次, 默认值为 false;
* trailing为true时,则在结束时触发, 默认值为 true;
* maxWait设置每隔多长时间必须触发;
*/
function foo () { ... }
jQuery('input').on('keyup', _.debounce(foo, 300, {
'leading': true, // 注意: 执行的前提是定时器为undefined
'trailing': false,
'maxWait': 1000
}));
// debounce还可以被取消
jQuery('button').on('click', function() {
validation.cancel();
});
throttle
function mousemove () {
...
}
jQuery('#div').on('mousemove', _.throttle(mousemove, 100)) // 每100ms必须触发一次
// mousemove会在定时器开始时触发, 但5分钟内不会有第二次
jQuery('#div').on('mousemove', _.throttle(mousemove, 300000, {
'leading': true, // 定时器前沿触发,默认值为true
'trailing': false // 定时器后沿触发,默认值为true
}));
// throttle 同样可以被取消
jQuery(window).on('popstate', throttled.cancel);