Source: functions/throttle.ts

import { TypeVoidFunction } from '../types';

/**
 * throttle
  <iframe
    src="https://codesandbox.io/embed/nonollcode-snippet-9gko8?autoresize=1&expanddevtools=1&fontsize=14&hidenavigation=1&initialpath=%2Ffunctions-throttle.html&module=%2Ffunctions-throttle.html&theme=dark"
    style="width:100%; height:500px; border:1px solid black; border-radius: 4px; overflow:hidden;"
    title="@nonoll/code-snippet"
    allow="geolocation; microphone; camera; midi; vr; accelerometer; gyroscope; payment; ambient-light-sensor; encrypted-media; usb"
    sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"
  ></iframe>
 * @memberof module:functions
 * @function
 * @see https://css-tricks.com/debouncing-throttling-explained-examples/
 * @param {TypeVoidFunction} fn
 * @param {Number} [delay=0]
 * @returns {TypeVoidFunction}
 * @example
import { debounce } from '@nonoll/code-snippet/functions';

const forExample = () => {
  const delay = 1000 * 1;
  const listener = throttle(res => console.log(res), delay);

  listener(`1 ${new Date()}`);
  listener('2');
  listener('3');
  listener('4');

  setTimeout(() => {
    listener(`5 ${new Date()}`);
  }, delay);
}

forExample();
 */
export const throttle = (fn: TypeVoidFunction, /* istanbul ignore next */ delay: number = 0): TypeVoidFunction => {
  const caller = (args: any[]) => {
    fn.apply(null, args);
    base = +new Date();
  };

  let base = null;
  let isLeading = true;
  let stamp = null;

  return (...args: any[]) => {
    if (isLeading) {
      caller(args);
      isLeading = false;
      return;
    }

    stamp = +new Date();
    /* istanbul ignore next */
    base = base || stamp;

    if ((stamp - base) >= delay) {
      /* istanbul ignore next */
      caller(args);
    }
  };
};