JS Polyfills - Part 4 Debounce & Throttle - Leading & Trailing options
Tags: javascript, webdev, jsitor, polyfill
Published: 2022-11-02 17:06:35

<link rel="canonical" href="https://dev.to/uttarasriya/js-polyfill-part-4-debounce-throttle-leading-trailing-options-3nn8" />
**Github Code:** [JS Polyfills](https://github.com/uttara-sriya/JS-Polyfills)
**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()**
- <u>Function</u>: `debounce(function, delay)`
- <u>Description</u>: Schedules a trigger for an event after specific interval.
- <u> [Lodash Implementation with more features](https://lodash.com/docs/4.17.15#debounce)</u>
- <u>Polyfill JS, HTML, Usecase result</u>: [Basic Debounce](https://jsitor.com/ByXL15AH4)

```javascript
/**
   * @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**

- <u>Function</u>: `debounce(callback, delay, option={leading:false, trailing: true})`
- <u>Description</u>: In short, If leading -> Capture initial click after delay, if trailing -> Capture latest click after a delay
- <u>Polyfill JS, HTML, Usecase result</u>: [Debounce Leading & Trailing](https://jsitor.com/4VcI6dKpw)

```javascript
/**
   * @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()**

- <u>Function</u>: `throttle(func, delay)`
- <u>Description</u>: Runs a given function just once in a given period
- <u> [Lodash Implementation with more features](https://lodash.com/docs/4.17.15#throttle)</u>
- <u>Polyfill JS, HTML, Usecase result</u>: [Throttle](https://jsitor.com/miVFEszee)

```javascript
/**
   * @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
  }
}

```
**14. Throttle with leading and trailing options**

- <u>Function</u>: `throttle(callback, delay, option={leading:false, trailing: true})`
- <u>Description</u>: leading/trailing indicates function should be run at the beginning of the stream of events or the end.
- <u>Polyfill JS, HTML, Usecase result</u>:[Throttle with leading and trailing](https://jsitor.com/o3C1Ts3U6)

```javascript
/**
   * @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);
  }
}
```
Keep Learning!