JS Polyfills - Part 4 Debounce & Throttle - Leading & Trailing options

JS Polyfills - Part 4 Debounce & Throttle - Leading & Trailing options

·

5 min read

What are Debounce and throttle ? They are techniques for optimizing browser performance in JS. Why we use them ? These are for limiting the rate of function execution/API request Where can we use them ? Input/button/window events, Searching/ Typeahead, Save as user inputs, hover actions, interactions while scrolling

11. Basic Debounce()

/**
   * @param {Function} customBasicDebounce
   * @param {Function} func
   * @param {number} millisec delay
   */

//What are we doing here? 
//1. Set the context
//2. If the timer exist, then clear the timer before calling the function
//3. If not, call the function
function customBasicDebounce(func, delay = 400) {
  //initalizing timer here - closure 
  let timer;
  return ((...args) => {
    //clearing timer before calling again for restricting multiple instances of timer
    clearTimeout(timer);
    //calls the function after delay
    timer = setTimeout(() => func.apply(this, ...args), delay);
  });
}

12. Debounce with leading & trailing options

  • Function: debounce(callback, delay, option={leading:false, trailing: true})

  • Description: In short, If leading -> Capture initial click after delay, if trailing -> Capture latest click after a delay

  • Polyfill JS, HTML, Usecase result: Debounce Leading & Trailing

/**
   * @param {Function} func
   * @param {number} delay
   * @param {boolean} option.leading
   * @param {boolean} option.trailing
   */
//What are we doing here? 
//1. Set the context and trailing arguments
//2. If not leading/trailing, return null
//3. If timer is done but leading is true, invoke the func execution
//4. If not, save the context for later execution
//5. clear the timer to avoid multiple timer instances 
//6. call the timer if trailing is true n trailing args exists
//7. Reset the timer and args
function customAdvancedDebounce(func, delay, option = { leading: false, trailing: true }) {

  let timer = null; // same like basic debounce
  let trailingArgs = null; // as we require last arguments for trailing 

  if (!option.leading && !option.trailing) return () => null; //if both false, return null

  return function debounced(...args) { //returns a debounced function

    if (!timer && option.leading) { // timer done but leading true
      func.apply(this, args); //call func
    } else {
      trailingArgs = args; // arguments will be the last args
    }

    clearTimeout(timer); //clear timer for avoiding multiple timer instances

    timer = setTimeout(() => {
      if (option.trailing && trailingArgs) func.apply(this, trailingArgs);  // trailingArgs is present and trailing is true

      trailingArgs = null; //reset last arguments
      timer = null; // reset timer
    }, delay);
  }
}

13. Basic Throttle()

/**
   * @param {Function} customBasicThrottle
   * @param {Function} func
   * @param {number} millisec delay
   */
//What are we doing here? 
//1. Set the context and trailing arguments
//2. If not executed before, execute now and update the context
//3. during exe, update the context and if trailing args exist, execute the function
//and reset the context and last args
//4. If executed, save the context for later execution

function customBasicThrottle(func, delay) {
  let lastRan = false, //last time the function got ex ecuted
      trailingArgs = null; //last arguments

  return function (...args) { //returns function
    if (!lastRan) { //if the function didnt get executed before
      func.apply(this, args) // call the function
      lastRan = true; //update the flag
      let timer = () => setTimeout(() => { //while executing 
        lastRan = false; //update the flag
        if (trailingArgs) { // if last arguments exist
          func.apply(this, trailingArgs); //invoke the function with those last arguments
          lastRan = true; //update the flag
          trailingArgs = null; //reset the arguments
          timer(); 
        }
      }, delay);
      timer(); 
    }
    else // if function got executed before
      trailingArgs = args //else update the last arguments with passed arguments
  }
}

12. Throttle with leading and trailing options

  • Function: throttle(callback, delay, option={leading:false, trailing: true})

  • Description: leading/trailing indicates function should be run at the beginning of the stream of events or the end.

  • Polyfill JS, HTML, Usecase result:Throttle with leading and trailing

/**
   * @param {Function} func
   * @param {number} delay
   * @param {boolean} option.leading
   * @param {boolean} option.trailing
   */
//What are we doing here? 
//1. Set the context, timer and trailing arguments
//2. if timer called within cooldown period, update context and save the trailing args for later execution
//3. If leading, execute the function
//4. If trailing, update context and save last args for later execution
//5. set cooldown period
//6. if trailing and the trailing args exist, invoke the timer, reset the cooldown period

const advThrottle = (func, delay, options = { leading: true, trailing: false }) => {
  let timer = null,
    lastRan = null,
    trailingArgs = null;

  return function (...args) {

    if (timer) { //called within cooldown period
      lastRan = this; //update context
      trailingArgs = args; //save for later
      return;
    } 

    if (options.leading) {// if leading
      func.call(this, ...args) //call the 1st instance
    } else { // else it's trailing
      lastRan = this; //update context
      trailingArgs = args; //save for later
    }

    const coolDownPeriodComplete = () => {
      if (options.trailing && trailingArgs) { // if trailing and the trailing args exist
        func.call(lastRan, ...trailingArgs); //invoke the instance with stored context "lastRan"
        lastRan = null; //reset the status of lastRan
        trailingArgs = null; //reset trailing arguments
        timer = setTimeout(coolDownPeriodComplete, delay) //clear the timout
      } else {
        timer = null; // reset timer
      }
    }

    timer = setTimeout(coolDownPeriodComplete, delay);
  }
}

Find more polyfills @ Github: JS Polyfills

Keep Learning!