javascript实现防抖动(Debouncing)和节流阀(Throttling)
实例解析防抖动(Debouncing)和节流阀(Throttling)
Javascript 的 Debounce 和 Throttle 的原理及实现 · Issue #7 · lishengzxc/bblog
应用场景还有,在一个表单的输入框中(包括多行文本输入框),想当用户停止输入后,再ajax:
input.addEventListener('keyup', debounce(() => ajax(...), 1000), false);
document.addEventListener('mousemove', debounce(() => console.log(new Date().getTime()), 1000), false);
防抖动(Debounce)
防抖技术可以把 多个快速连续触发的事件合并成一次,并延迟调用.
直接使用 underscore 或 Lodash 。如果仅需要 _.debounce 和 _.throttle 方法,可以使用 Lodash 的自定义构建工具,生成一个 2KB 的压缩库。使用以下的简单命令即可:
npm i -g lodash-cli
lodash-cli include=debounce,throttle
常见的坑是,不止一次地调用 _.debounce 方法:
// 错误
$(window).on('scroll', function() {
_.debounce(doSomething, 300);
});
// 正确
$(window).on('scroll', _.debounce(doSomething, 200));
debounce 方法保存到一个变量以后,就可以用它的私有方法 debounced_version.cancel(),lodash 和 underscore.js 都支持。
var debounced_version = _.debounce(doSomething, 200);
$(window).on('scroll', debounced_version);
// 如果需要的话
debounced_version.cancel();
loadsh的debounce参数:
leading=false: Specify invoking on the leading edge of the timeout.
maxWait: The maximum time func is allowed to be delayed before it’s invoked.
trailing=true: Specify invoking on the trailing edge of the timeout.
loadash的 leading=true等效于underscore的immediate=true
Throttle(节流阀)
throttle就是设置固定的函数执行速率,从而降低频繁事件回调的执行次数
使用 _.throttle 的时候,只允许一个函数在 X 毫秒内执行一次。
跟 debounce 主要的不同在于,throttle 保证 X 毫秒内至少执行一次。
requestAnimationFrame 是另一种限速执行的方式。
跟 _.throttle(dosomething, 16) 等价。它是高保真的,如果追求更好的精确度的话,可以用浏览器原生的 API 。
window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
/**
* Handles debouncing of events via requestAnimationFrame
* @see http://www.html5rocks.com/en/tutorials/speed/animations/
* @param {Function} callback The callback to handle whichever event
*/
function Debouncer (callback) {
this.callback = callback;
this.ticking = false;
}
Debouncer.prototype = {
constructor : Debouncer,
// dispatches the event to the supplied callback
update : function() {
this.callback && this.callback();
this.ticking = false;
},
// ensures events don't get stacked
requestTick : function() {
if(!this.ticking) {
requestAnimationFrame(this.rafCallback || (this.rafCallback = this.update.bind(this)));
this.ticking = true;
}
},
handleEvent : function() {
this.requestTick();
}
};