How do I add a delay in a JavaScript loop?

How do I add a delay in a JavaScript loop?

I would like to add a delay/sleep inside a while loop:
I tried it like this:
alert(‘hi’);

for(var start = 1; start < 10; start++) { setTimeout(function () { alert('hello'); }, 3000); } Only the first scenario is true: after showing alert('hi'), it will be waiting for 3 seconds then alert('hello') will be displayed but then alert('hello') will be repeatedly constantly. What I would like is that after alert('hello') is shown 3 seconds after alert('hi') then it needs to wait for 3 seconds for the second time alert('hello') and so on.

Solutions/Answers:

Solution 1:

The setTimeout() function is non-blocking and will return immediately. Therefore your loop will iterate very quickly and it will initiate 3-second timeout triggers one after the other in quick succession. That is why your first alerts pops up after 3 seconds, and all the rest follow in succession without any delay.

You may want to use something like this instead:

var i = 1;                     //  set your counter to 1

function myLoop () {           //  create a loop function
   setTimeout(function () {    //  call a 3s setTimeout when the loop is called
      alert('hello');          //  your code here
      i++;                     //  increment the counter
      if (i < 10) {            //  if the counter < 10, call the loop function
         myLoop();             //  ..  again which will trigger another 
      }                        //  ..  setTimeout()
   }, 3000)
}

myLoop();                      //  start the loop

You could also neaten it up, by using a self invoking function, passing the number of iterations as an argument:

(function myLoop (i) {          
   setTimeout(function () {   
      alert('hello');          //  your code here                
      if (--i) myLoop(i);      //  decrement i and call myLoop again if i > 0
   }, 3000)
})(10);                        //  pass the number of iterations as an argument

Solution 2:

Try something like this:

var i = 0, howManyTimes = 10;
function f() {
    alert( "hi" );
    i++;
    if( i < howManyTimes ){
        setTimeout( f, 3000 );
    }
}
f();

Solution 3:

If using ES6, you could use let to achieve this:

for (let i=1; i<10; i++) {
    setTimeout( function timer(){
        alert("hello world");
    }, i*3000 );
}

What let does is declare i for each iteration, not the loop. This way, what is passed to setTimeout is exactly what we want.

Solution 4:

Since ES7 theres a better way to await a loop:

function timer(ms) {
 return new Promise(res => setTimeout(res, ms));
}

async function load () {
  for (var i = 0; i < 3; i++) {
    console.log(i);
    await timer(3000);
  }
}

load();

Reference on MDN

Note that ES7 is rarely supported today, so you need to transpile with Babel to use it everywhere.

Transpiled

Solution 5:

Another way is to multiply the time to timeout, but note that this is not like sleep. Code after the loop will be executed immediately, only the execution of the callback function is deferred.

for (var start = 1; start < 10; start++)
    setTimeout(function () { alert('hello');  }, 3000 * start);

The first timeout will be set to 3000 * 1, the second to 3000 * 2 and so on.

Solution 6:

I think you need something like this:

var TimedQueue = function(defaultDelay){
    this.queue = [];
    this.index = 0;
    this.defaultDelay = defaultDelay || 3000;
};

TimedQueue.prototype = {
    add: function(fn, delay){
        this.queue.push({
            fn: fn,
            delay: delay
        });
    },
    run: function(index){
        (index || index === 0) && (this.index = index);
        this.next();
    },
    next: function(){
        var self = this
        , i = this.index++
        , at = this.queue[i]
        , next = this.queue[this.index]
        if(!at) return;
        at.fn();
        next && setTimeout(function(){
            self.next();
        }, next.delay||this.defaultDelay);
    },
    reset: function(){
        this.index = 0;
    }
}

Test code:

var now = +new Date();

var x = new TimedQueue(2000);

x.add(function(){
    console.log('hey');
    console.log(+new Date() - now);
});
x.add(function(){
    console.log('ho');
    console.log(+new Date() - now);
}, 3000);
x.add(function(){
    console.log('bye');
    console.log(+new Date() - now);
});

x.run();

Note: using alerts stalls javascript execution till you close the alert.
It might be more code than you asked for, but this is a robust reusable solution.