Asins Blog

简单其实不简单

This project is maintained by asins

/*
 * 防抖动(debounce)如果电梯里有人进来,等待15秒。如果又人进来,15秒等待重新计时,直到15秒超时,开始运送。
 * atBegin值带来的callback执行规律,__表示delay |表示callback执行一次
 *     false   ______|
 *     true   |______
 */
export function debounce(delay, callback, atBegin = false) {
  return _throttleDebounce(delay, callback, false, atBegin);
}

/*
 * 节流阀(throttle)保证如果电梯第一个人进来后,15秒后准时运送一次,不等待。如果没有人,则待机。
 * noTrailing值带来的callback执行规律,__表示delay  |表示callback执行一次
 *     false   __|__|__|
 *     true   |__|__|__
 */
export function throttle(delay, callback, noTrailing = false) {
  return _throttleDebounce(delay, callback, noTrailing);
}

/**
 * Throttle execution of a function. Especially useful for rate limiting
 * execution of handlers on events like resize and scroll.
 *
 * @param  {Number}    delay          0毫秒或更大的延迟。事件回调,在100或250值(甚至更高)是最有用的。
 * @param  {Boolean}   noTrailing     可选,默认为false, 如果为true,callback只会在throttled函数执行间隔`delay`毫秒后才调用一次;
 *                                    如果为false或未指定,callback将在最后一次throttled函数执行后调用。(`delay`毫秒后无新的throttled函数被执行则认为是最后一次)
 * @param  {Function}  callback       在`delay`毫秒后执行的函数,`this`上下文以及所有参数会在throttled函数执行后传递给`callback`。
 * @param  {Boolean}   debounceMode   If `debounceMode` is true (at begin), schedule `clear` to execute after `delay` ms. If `debounceMode` is false (at end),
 *                                    schedule `callback` to execute after `delay` ms.
 *
 * @return {Function}  一个新的throttled函数
 */
function _throttleDebounce(delay, callback, noTrailing, debounceMode) {

  let timeoutID;

  let lastExec = 0;

  function wrapper() {
    const self = this;
    const elapsed = +new Date() - lastExec;
    const args = arguments;

    function exec() {
      lastExec = +new Date();
      callback.apply(self, args);
    }

    function clear() {
      timeoutID = undefined;
    }

    if (debounceMode && !timeoutID) {
      exec();
    }

    if (timeoutID) {
      clearTimeout(timeoutID);
    }

    if (debounceMode === undefined && elapsed > delay) {
      exec();
    } else if (noTrailing !== true) {
      timeoutID = setTimeout(debounceMode ? clear : exec, debounceMode === undefined ? delay - elapsed : delay);
    }
  }

  return wrapper;
}