JavaScript's setTimeout function is widely used to schedule tasks to be executed after a specified delay. However, understanding how such a function can be built from scratch helps you deepen your grasp of JavaScript's asynchronous nature. In this blog, we'll explore a polyfill for setTimeout using a custom implementation, mySetTimeout, and its counterpart, myClearTimeout.

Here's the code for our polyfill:

function createSetTimeout() {
  let timerId = 0;
  let timerMap = {};

  function mySetTimeout(cb, time, ...args) {
    var id = timerId++;
    timerMap[id] = true;
    let start = Date.now();

    function triggerCallback() {
      if (!timerMap[id]) return;
      if (Date.now() > start + time) {
        cb.apply(this, args);
      } else {
        requestIdleCallback(triggerCallback);
      }
    }
    requestIdleCallback(triggerCallback);
    return id;

  }

  function myClearTimeout(id) {
    delete timerMap[id];
  }
  return {
    mySetTimeout,
    myClearTimeout
  }

}
const {
  mySetTimeout,
  myClearTimeout
} = createSetTimeout();
let timer = mySetTimeout((...args) => {
  console.log("this is setTimeout.....", ...args);
}, 1000, "satish", "shriwas");

Step-by-Step Explanation

1. createSetTimeout Function:

This function serves as the main setup for our polyfill. It encapsulates the logic and maintains state using closures. Inside this function:

  • timerId is initialized to 0. It will be used to generate unique IDs for each timer.
  • timerMap is an object that stores active timers. It helps manage and clear timers when needed.

2. mySetTimeout Function:

This function mimics the behavior of setTimeout. Here's how it works:

  • Timer ID Management:
  • A unique id is generated for each timer using timerId++.
  • The timer ID is stored in timerMap, marked as true to indicate that it's active.
  • Time Calculation:
  • start captures the current time using Date.now(). This value is used to calculate the elapsed time.
  • Callback Execution (triggerCallback):
  • This function is responsible for executing the callback function cb when the specified time has passed.
  • It checks if the current time (Date.now()) is greater than the start time plus the specified delay (time). If true, it triggers the callback using cb.apply(this, args) to ensure the correct context and arguments are passed.
  • If the time hasn't elapsed yet, it is used requestIdleCallback to recursively check again when the browser is idle.
  • Returning the Timer ID:
  • The function returns the unique id generated for the timer. This ID can be used to clear the timer later.

3. myClearTimeout Function:

This function allows you to clear a timer before it executes, just like the native clearTimeout.

  • It takes a timer ID as an argument and deletes the corresponding entry from timerMap. This prevents the callback from being executed.

4. Using mySetTimeout and myClearTimeout:

Finally, you can use mySetTimeout to schedule tasks, and myClearTimeout to cancel them. In the example provided:

let timer = mySetTimeout((...args) => {
  console.log("this is setTimeout.....", ...args);
}, 1000, "satish", "shriwas");

A timer is set to log a message to the console after 1 second, along with the arguments "satish" and "shriwas".

Key Points:

  • requestIdleCallback: This method is used to defer the execution of triggerCallback until the browser is idle, making it more efficient for low-priority background tasks. However, it might not be as precise as the native setTimeout, making this polyfill better suited for non-critical timing tasks.
  • Closure: The polyfill uses closures to maintain the state (timerId and timerMap) across multiple calls to mySetTimeout and myClearTimeout.

Conclusion:

This custom polyfill setTimeout demonstrates how you can implement asynchronous behavior using JavaScript's built-in functions. While the native setTimeout is optimized and should be your go-to choice, understanding and implementing such polyfills can significantly improve your JavaScript skills, especially when dealing with asynchronous code and browser event loops.

This implementation provides a basic framework that you can expand and adapt based on your needs. Happy coding!

In Plain English ๐Ÿš€

Thank you for being a part of the In Plain English community! Before you go: