21

I want that my for loop should not be executed at once, but wait for timeout after each iteration. For eg :

for(var i=0; i<10; i++) {
    console.log(i);
    //wait for 1000
}

I found many solutions on stack-overflow like this one :

for (var i=0;i<=10;i++) {
   (function(ind) {
       setTimeout(function(){console.log(ind);}, 3000);
   })(i);
}

But in all the implementations, the loop waits for 3000 milli-seconds initially and then executes the whole for loop at once. Is there a way that each iteration is called after waiting for 1000 milli-seconds.

Parag Gangil
  • 1,098
  • 2
  • 10
  • 25
  • 1
    Javascript doesn't have methods to pause execution for x seconds and then continue with the next line. It simply doesn't work that way. Instead you must break your work into chunks of work and then schedule the next chunk using `setTimeout()` or `setInterval()` or some other event which triggers a callback. – jfriend00 Jun 18 '14 at 19:40
  • Possible duplicate of [setTimeout in for-loop does not print consecutive values](http://stackoverflow.com/questions/5226285/settimeout-in-for-loop-does-not-print-consecutive-values) – Oleg Dec 12 '15 at 21:24

12 Answers12

36

You can work that out with simple math :

for (var i=0;i<=10;i++) {
   (function(ind) {
       setTimeout(function(){console.log(ind);}, 1000 + (3000 * ind));
   })(i);
}

1000ms : 0
4000ms : 1
7000ms : 2
10000ms : 3
13000ms : 4
...


Following the comments

It seem that your request is a bit blurry. if you want to do something after the last timeout, you can set a limit and compare the current index :

var limit = 10
for (var i=0;i<=limit;i++) {
   (function(ind) {
       setTimeout(function(){
           console.log(ind);
           if(ind === limit){
               console.log('It was the last one');
           }
       }, 1000 + (3000 * ind));
   })(i);
}

Fiddle : http://jsfiddle.net/Tn4A7/


I think I know what you want...

and it is to simply do

for (var i=0;i<=10;i++) {
   (function(ind) {
       setTimeout(function(){console.log(ind);}, 1000 * ind);
   })(i);
}
Community
  • 1
  • 1
Karl-André Gagnon
  • 32,531
  • 5
  • 47
  • 70
  • I'm not sure that's exactly what the OP wants either (although it's definitely better). I think they want to wait until all the timeouts have fired and then continue. – Matt Burland Jun 18 '14 at 19:25
  • Maybe this doesn't matter - and it's certainly not Gagnon's fault, this is how the question presented it - but you do realize that the execution of the script is long past the for-loop even before the first console.log right? **See: http://jsfiddle.net/WXkLK/** – myfunkyside Jun 18 '14 at 19:30
  • @Karl-AndréGagnon - I know, I know, that's why I started off with clearing you of any blame – myfunkyside Jun 18 '14 at 19:45
  • @Karl-André Gagnon you mind telling me why the (i); ??? I've understood everything, except that part, I mean, I don't understand what is it doing there, what it does, or why is there. If I just put this: for (var i=0;i<=10;i++) { (i); } Obviously it doesn't does anything, but, in the function I supressed it shouldn't modify anything either, as it's not mentioned anywhere... My point is, the "(i);" shouldn't do anything in any case. I'm completely lost with that (i); If you could clarify me, or tell me what to look for (that's the worst part), I would really appreciate it. – Fabian Montossi Jan 15 '21 at 12:25
  • 1
    @FabianMontossi The `(i)` immediately invokes the anonymous function inside the previous parentheses. In this case, `(function(ind) {...})(i)` creates a new function that accepts an argument `ind` and then immediately calls it with the value of `i` **before** going to the next iteration of the loop. So if the value of `i` changes, `ind` will not. That's called an IIFE, you can read more here: https://developer.mozilla.org/en-US/docs/Glossary/IIFE – Karl-André Gagnon Jan 18 '21 at 14:59
  • Oooh, ok, I see, thank you! – Fabian Montossi Jan 19 '21 at 06:58
17

Don't make functions within loops, instead:

(function fiveSeconds  (n) {

  if (n < 5) setTimeout(function () {  
    fiveSeconds ( n ); // Redo if n < 5 (and pass n)
  }, 1000);
  
  console.log( n++ );

} (0)); // Initialize. n is 0

the above will log ten numbers from 0 - 5 at 1 seconds interval.

Modern browsers (and IE10+)

(function fiveSeconds (n) {

  console.log( n++ );

  if (n <= 5) setTimeout( fiveSeconds, 1000, n ); // Redo if n <= 5 (and pass n)
  
} (0)); // Initialize. n is 0
Roko C. Buljan
  • 164,703
  • 32
  • 260
  • 278
6

why not use something like this:

var i = 0
var id = window.setInterval(function(){
    if(i >= 10) {
        clearInterval(id);
        return;
    }

    console.log(i);
    i++;
}, 1000)
CannibalGorilla
  • 144
  • 2
  • 8
3

This works:

function initiateTimeOut(i) {
  setTimeout(function() { doStuff(i) }, 30);
}
function doStuff(i) {
    console.log(i);
    i++;
    if (i <= 10) {
        initiateTimeOut(i); 
    }
}

initiateTimeOut(0);

this way you will only increment i when your function executes, which i believe is what your looking for.

Example in a fiddle: http://jsfiddle.net/My7Zg/


Or, even shorter (http://jsfiddle.net/My7Zg/1/):

function customLoop(i) {
    console.log(i);
    i++;
    if (i<=10) {setTimeout(function(){customLoop(i);},1000);}
}
customLoop(0);
myfunkyside
  • 3,630
  • 1
  • 15
  • 30
agconti
  • 15,820
  • 15
  • 69
  • 108
2

You can approach your situation in two ways.

  1. You can immedately schedule a whole bunch of setTimeout() calls with varying times so they will execute at the desired times in the future (other answers here illustrate how to do that).

  2. You can execute the first iteration, schedule the next iteration and have the execution of the next iteration schedule the one after that until you've finished the desired number of iterations. This is ultimately a bit more scalable than setting a lot of setTimeout() call and gives you more branching/logic freedom because you are in control of what happens next after each iteration.

This second option using a more general purpose utility function would look like this:

// utility function to call a callback numTimes, 
// separated by delay milliseconds
function runIteration(fn, numTimes, delay) {
    var cnt = 0;
    function next() {
        // call the callback and stop iterating if it returns false
        if (fn(cnt) === false) return;
        ++cnt;
        // if not finished with desired number of iterations,
        // schedule the next iteration
        if (cnt < numTimes) {
            setTimeout(next, delay);
        }
    }
    // start first iteration
    next();

}

So, to execute your console statement, you'd do this:

runIteration(function(i) {
    console.log(i);
}, 10, 1000);

Working demo: http://jsfiddle.net/jfriend00/HqCZ3/

This could also be extended with a 2nd callback function that was called when the iteration was complete (useful in some circumstances) or it could return a promise that is resolved when the iterations are complete.

Here's what a version that returns a promise would look like: http://jsfiddle.net/jfriend00/XtJ69/

// utility function to call a callback numTimes, 
// separated by delay milliseconds
function runIteration(fn, numTimes, delay) {
    var d = $.Deferred();
    var cnt = 0;

    function end() {
        d.resolve();
    }

    function next() {
        // call the callback and stop iterating if
        // it returns false
        if (fn(cnt) === false) {
            end();
            return;
        }
        ++cnt;
        // if not finished with desired number of iterations,
        // schedule the next iteration
        if (cnt < numTimes) {
            setTimeout(next, delay);
        } else {
            end();
        }
    }
    // start first iteration
    next();
    return d.promise();
}


runIteration(function(i) {
    log(i);
}, 10, 1000).done(function() {
    log("done");
});
jfriend00
  • 580,699
  • 78
  • 809
  • 825
1
for (var i=0;i<=10;i++) {
   (function(ind) {
       setTimeout(function(){console.log((ind + 1)*1000, ':', ind);}, 1000 * (ind+1) );
   })(i);
}

Output:

1000 : 0
2000 : 1
3000 : 2
4000 : 3
5000 : 4
6000 : 5
7000 : 6
8000 : 7
9000 : 8
10000 : 9
11000 : 10

WORKING DEMO

PeterKA
  • 21,234
  • 4
  • 21
  • 44
1

Here is an es6 solution. I really don't like wrapping the setTimeout in a function, when you can simply use a block scoped variable like this:

for (let i=0; i<=10; i++) {
    setTimeout(() => {console.log(i);}, 1000 * i);
}
Artur Grigio
  • 3,989
  • 6
  • 34
  • 56
  • I was about to answer this question with a solution exactly like yours. However, it should be noted that this solution does not exactly do what the OP wanted, which was to wait 1000ms before each loop iteration executes. What this does is waits 1000ms for the 1st iteration, 2000ms for 2nd iteration, 3000ms for the 3rd and so on, giving the illusion of waiting 1000ms before each execution. – gig6 Apr 27 '21 at 01:54
0

This is a solution with a simple timeout... Maybe it does not match exactly with what you expect, but trying to make a "pause" with javascript is not a good approach in my advice. I suggest you to search an other way to do what you want. Fiddle

window.my_condition = true;
window.my_i = 0;

function interate() {
    console.log(window.my_i);
    // ... your code
    if (window.my_condition!==false) {
        window.my_i++;
        setTimeout(interate,300);
    }
}

interate();
weeger
  • 410
  • 2
  • 9
0

most of the answers in here are completely wrong.

If you want to wait for each iteration to finish --- then you don't want to use a for loop --- simply the wrong strategy to begin with.

you need to use a counter and a counter limit otherwise it will loop endlessly.

here is the solution:

var optionLimit = 11;
var optionItem = 1;
function do_something_else() {
    if (optionItem < optionLimit) {
        console.log('doing stuff:' + optionItem)
        optionItem++
        dostuff();
    } else {
        console.log('no more stuff to do already reached:' + optionItem)
    }
}
function dostuff(started) {
    if (started) {
        console.log('started doing something');
    } else {
        console.log('find something else to do');
    }
    setTimeout(function () {
        do_something_else();
    }, 3000);
}
dostuff('started doing something');

if you have a set of items that you need to index --- then you can use a loop to count through the number of items that need to be executed like so:

var thingstodo = [
    thing1 = {
        what: 'clean room',
        time: 8000
    },
    thing2 = {
        what: 'laundry',
        time: 9000
    },
    thing3 = {
        what: 'take out trash',
        time: 6000
    },
    thing4 = {
        what: 'wash dishes',
        time: 10000
    }
]
var optionLimit = 0;
// find how many things to do from things to do list
function get_things_todo(time) {
    console.log('heres stuff i can do');
    console.log('====================');
    for (var i = 0; i < thingstodo.length; i++) {
        val = thingstodo[i];
        console.log(JSON.stringify(val.what));
        optionLimit++
    }
    setTimeout(function () {
        startdostuff(3000)
    }, time);
}
var optionItem = 0;
// find the next thing to do on the list
function get_next_thing(time) {
    setTimeout(function () {
        console.log('================================');
        console.log('let me find the next thing to do');
    }, time);
    setTimeout(function () {
        if (optionItem < optionLimit) {            
            val = thingstodo[optionItem];            
            dostuff(3000, val);
            optionItem++
        } else {
            console.log('=====================================================');
            console.log('no more stuff to do i finished everything on the list')
        }
    }, time*1.5);
}
//do stuff with a 3000ms delay
function dostuff(ftime, val) {
    setTimeout(function () {
        console.log('================================');
        console.log('im gonna ' + JSON.stringify(val.what));
        console.log('will finish in: ' + JSON.stringify(val.time) + ' milliseconds');
        setTimeout(function () {
            console.log('========');
            console.log('all done');
            get_next_thing(3000);
        }, val.time);
    }, ftime);
}
//start doing stuff
function startdostuff(time) {
    console.log('========================');
    console.log('just started doing stuff');
    setTimeout(function () {
        get_next_thing(3000);
    }, time);
}
/// get things to first
get_things_todo(3000);
Michael P.
  • 69
  • 4
0

My best way in work is to "forget normal loops" in this case and use this combination of "setInterval" includes "setTimeOut"s:

    function iAsk(lvl){
        var i=0;
        var intr =setInterval(function(){ // start the loop 
            i++; // increment it
            if(i>lvl){ // check if the end round reached.
                clearInterval(intr);
                return;
            }
            setTimeout(function(){
                $(".imag").prop("src",pPng); // do first bla bla bla after 50 millisecond
            },50);
            setTimeout(function(){
                 // do another bla bla bla after 100 millisecond.
                seq[i-1]=(Math.ceil(Math.random()*4)).toString();
                $("#hh").after('<br>'+i + ' : rand= '+(Math.ceil(Math.random()*4)).toString()+' > '+seq[i-1]);
                $("#d"+seq[i-1]).prop("src",pGif);
                var d =document.getElementById('aud');
                d.play();                   
            },100);
            setTimeout(function(){
                // keep adding bla bla bla till you done :)
                $("#d"+seq[i-1]).prop("src",pPng);
            },900);
        },1000); // loop waiting time must be >= 900 (biggest timeOut for inside actions)
    }

PS: Understand that the real behavior of (setTimeOut): they all will start in same time "the three bla bla bla will start counting down in the same moment" so make a different timeout to arrange the execution.

PS 2: the example for timing loop, but for a reaction loops you can use events, promise async await ..

Mohamed Abulnasr
  • 497
  • 7
  • 12
0

I share a simple solution to do this.

To solve this problem, you need to use closure: immediately invoke the function that will be called on each iteration with "i" as param and setTimeout inside this function. In this case, the parameter you passed will be stored in scope and could be used in the timeout callback:

for (var i=1; i<6; i++) (function(t) {
    setTimeout(function() {
    //do anything with t
    }, t*1000)
}(i))

With this example you would see approximately what happens with the function:

for (var i=1; i<6; i++) (function(t) {
    setTimeout(function() {
    console.log(t);
    }, t*1000)
}(i))
Juan
  • 167
  • 6
0

Another workaround is to use a generator function with setInterval:

const steps = function*() {
  for (let i=0; i<10; i++) {
    yield i;
  }
}

const step = steps();
setInterval(function(){ 
    console.log(step.next().value)
}, 1000);
Peyman Kheiri
  • 335
  • 3
  • 18