Time-delayed function queue I recently had an interesting requirement in a test environment where I needed to simulate actions at varied (random) intervals in a node back-end. They needed to be sequentially executed, but queued so I could fire and move on. Using a typical timeout/interval here wouldn't work, since if I set a timeout for fn1 to execute after 500ms and then set a timeout for fn2 to execute after 250ms, fn2 would execute before fn1, which was not desired.

To solve this, I created a queue object that executes queued commands sequentially. It works perfectly for my scenario, but I was wondering if there were any opportunities for improvement. I realize I should probably be checking input parameters to make sure the passed in function is indeed a function, as well as the delay being a number. Maybe there are other opportunities for improvement. Let me know!

var TimerQueue = (function () {
    var timers = [];
    var running = false;
    var currentInterval;
    var currentTimer;

    this.addTimer = function (fn, delay) {
        timers.push({fn: fn, delay: delay});

        function exec() {
            currentTimer.fn();
            clearInterval(currentInterval);
            if (timers.length &gt; 0) {
                currentTimer = timers.shift();
                currentInterval = setInterval(exec, currentTimer.delay);
            } else {
                running = false;
            }
        }

        if (!running) {
            running = true;
            currentTimer = timers.shift();
            currentInterval = setInterval(exec, currentTimer.delay);
        }
    };

    this.clear = function () {
        if (currentInterval) {
            clearInterval(currentInterval);
        }
        timers = [];
        running = false;
    };

    return this;
});


Usage:

var queue = new TimerQueue();
queue.addTimer(function () { console.log('1st') }, 500);
queue.addTimer(function () { console.log('2nd') }, 250);
I would do it as a singleton, so that the core functionality can be protected and private. All that is exposed is the Interface, .add(),start(),clear(),and the semaphore .done

Using prototype is only an advantage if the object that it belongs to is instanced many times. If you only create the object a few times, or once then adding to the prototype is actually more work for the javascript engine.

I also added a start method so that the queue could be created and started when needed. Once started you can add to the queue as long as done === false

// singleton. Assuming you only need one copy of timed queue
const timedQueue = (function(){
    var API;                // internal referance to interface
    const queue = [];       // array to hold functions
    var task = null;        // the next task to run
    var tHandle;            // To stop pending timeout 
    function next(){  // runs current scheduled task and  creates timeout to schedule next
        if(task !== null){          // is task scheduled??
            task.func();            // run it
            task = null;            // clear task
        }
        if(queue.length &gt; 0){       // are there any remain tasks??
            task = queue.shift();   // yes set as next task
            tHandle = setTimeout(next,task.time) // schedual when
        }else{
            API.done = true;
        }
    }
    return API = {
        add : function(func,time){
            queue.push({func : func, time: time});
        },
        start : function(){
            if(queue.length &gt; 0 &amp;&amp; API.done){
                API.done = false;   // set state flag
                tHandle = setTimeout(next,0);
            }
        },
        clear : function(){
            task = null;            // remove pending task
            queue.length = 0;       // empty queue
            clearTimeout(tHandle);  // clear timeout
            API.done = true;        // set state flag
        },
        done : true,
    }
})();


// usage.
function test1(){
    console.log("Test1 run");
}
function test2(){
    console.log("Test2 run");
}

timedQueue.add(test1,1000)
timedQueue.add(test2,1000)
timedQueue.add(test1,3000)
timedQueue.add(test2,100)
timedQueue.start();