javascript实现防抖动(Debouncing)和节流阀(Throttling)


原文链接: 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();
  }
};
`