Create a fake setTimeout in JavaScript

Create a fake setTimeout that should work similar to the original setTimeout method.

MY_TIMER.setTimeout(() => {
  console.log(1)
}, 2500);

MY_TIMER.setTimeout(() => {
  console.log(2)
}, 2000);

MY_TIMER.run();

Output:
2 // will be printed after 2 seconds
1 // will be printed 500 milliseconds after the 1st.

Well, there is no definite way to implement the custom setTimeout, but we can do a workaround.

Create a custom object with that will have setTimeout, clearTimeout, and run method.

In the setTimeout, store each entry in the queue, for the delay add the input to the current time, to determine when it should be invoked. Also after each entry sort the queue in ascending order based on the time. Return a unique id at the end.

Using the timer id, we can remove the entry from the queue in clearTimeout.

In the run method, we will run an infinite while loop, in each iteration, we will get the first element from the queue (as it will be with the least time), check if its time is past the current time then invoke it, else push it back into the queue.

Do it for all the entries in the queue. Add a condition to check if there are no more timers (the queue is empty) to run then break the loop.

const MY_TIMER = {
  timerId: 1,
  queue: [],
  // create a new timer
  setTimeout: function(cb, time, ...args){
    const id = this.timerId++;
    
    // add a new entry to the queue
    // the time at which it will run
    // will be added to the current date
    // so that it will run next
    this.queue.push({
      id,
      cb,
      time: Date.now() + time,
      args,
    });
    
    // sort the queue in the ascending order of time
    this.queue.sort((a, b) => a.time - b.time);
    
    // return the id
    return id;
  },
  
  // to stop the timer
  clearTimeout: function(removeId) {
      // remove the entry with the given id
      this.queue = this.queue.filter(({ id }) => id !== removeId);
  },
  
  // start running the timer
  run: function() {
    
    // this will continuously run the loop
    // till all the entry in the queue are invoked
    while(true) {
      const entry = this.queue.shift();
      const { cb, time, args } = entry;
      
      // if time hass passed
      // invoke it
      if(Date.now() > time){
        cb(...args);
      }
      // else push it back into the queue
      else{    
        this.queue.unshift(entry);
      }
      
      // if there are no further entries
      // break the loop
      if(this.queue.length === 0){
        break;
      }
    }
  }
};
Input:
MY_TIMER.setTimeout(() => {
  console.log(1)
}, 2500);
MY_TIMER.setTimeout(() => {
  console.log(2)
}, 2000);
MY_TIMER.setTimeout(() => {
  console.log(3)
}, 2500);
MY_TIMER.setTimeout(() => {
  console.log(4)
}, 3000);

MY_TIMER.run();

Output:
2
1
3
4