Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Looping setTimeout

I'm currently trying to wrap my head around some JavaScript.

What I want is a text to be printed on the screen followed by a count to a given number, like so:

"Test"

[1 sec. pause]

"1"

[1 sec. pause]

"2"

[1 sec. pause]

"3"

This is my JS:

$(document).ready(function() {

    var initMessage = "Test";
    var numberCount = 4;    

function count(){

    writeNumber = $("#target");

    setTimeout(function(){
        writeNumber.html(initMessage);
    },1000);

        for (var i=1; i < numberCount; i++) {

    setTimeout(function(){
        writeNumber.html(i.toString());
    },1000+1000*i)};

};

 count();

});

This is my markup:

<span id="target"></span>

When I render the page, all I get is "Test" followed by "4".

I'm no JavaScript genius, so the solution could be fairly easy. Any hints on what is wrong is highly appreciated.

You can play around with my example here: http://jsfiddle.net/JSe3H/1/

like image 747
timkl Avatar asked Nov 20 '11 14:11

timkl


3 Answers

You have a variable scope problem. The counter (i) inside the loop is only scoped to the count function. By the time the loop has finished executing, is value is 4. This affects every setTimeout function, which is why you only ever see "4".

I would rewrite it like this:

function createTimer(number, writeNumber) {
    setTimeout(function() {
        writeNumber.html(number.toString());
    }, 1000 + 1000 * number)
}

function count(initMessage, numberCount) {
    var writeNumber = $("#target");

    setTimeout(function() {
        writeNumber.html(initMessage);
    }, 1000);

    for (var i = 1; i < numberCount; i++) {
        createTimer(i, writeNumber);
    }
}

$(document).ready(function() {

    var initMessage = "Test";
    var numberCount = 4;


    count(initMessage, numberCount);

});

The createTimer function ensures that the variable inside the loop is "captured" with the new scope that createTimer provides.

Updated Example: http://jsfiddle.net/3wZEG/

Also check out these related questions:

  • What's going on under the hood here? Javascript timer within a loop
  • JavaScript closure inside loops – simple practical example
like image 66
Andrew Whitaker Avatar answered Sep 24 '22 19:09

Andrew Whitaker


In your example, you're saying "2, 3, 4 and 5 seconds from now, respectively, write the value of i". Your for-loop will have passed all iterations, and set the value of i to 4, long before the first two seconds have passed.

You need to create a closure in which the value of what you're trying to write is preserved. Something like this:

for(var i = 1; i < numberCount; i++) {
    setTimeout((function(x) {
        return function() {
            writeNumber.html(x.toString());
        }
    })(i),1000+1000*i)};
}

Another method entirely would be something like this:

var i = 0;
var numberCount = 4;

// repeat this every 1000 ms
var counter = window.setInterval(function() {

   writeNumber.html( (++i).toString() );

   // when i = 4, stop repeating
   if(i == numberCount)
       window.clearInterval(counter);

}, 1000);
like image 34
David Hedlund Avatar answered Sep 24 '22 19:09

David Hedlund


Hope this helps:

var c=0;
var t;
var timer_is_on=0;

function timedCount()
{
document.getElementById('target').value=c;
c=c+1;
t=setTimeout("timedCount()",1000);
}

function doTimer()
{
if (!timer_is_on)
  {
  timer_is_on=1;
  timedCount();
  }
}
like image 35
Sudhir Bastakoti Avatar answered Sep 26 '22 19:09

Sudhir Bastakoti