Question

I just want to print out a value in for loop after 1 sec, but when I run the code in firebug I get unexpected value printed out each time I run the code. Not sure what is happening.

for(var k=0; k<3; k++) {
setTimeout(function() { console.log(k); },1000);
}

I expected after 1 sec: 0 1 2

Instead I get this: 3635 3

Was it helpful?

Solution

You need to enclose a copy of the k variable inside your function, otherwise your functions will refer to the k variable outside which by the time the function executes has the value 3:

for(var k=0; k<3; k++) {
  (function(k_copy) {
    setTimeout(function() { console.log(k_copy); },1000);
  })(k); // immediately execute
}

Alternative using Function.prototype.bind:

for(var k=0; k<3; k++) {
  setTimeout(function(k_copy) { console.log(k_copy); }.bind(null,k),1000);
}

OTHER TIPS

I would recommend looking up some resources on Asynchronous JavaScript, as understanding that would help in this situation. JavaScript is single-threaded, and will wait before calling the callback functions. Think of it this way:

  1. k=0; enter the loop, call setTimeout() which is an async function; callback function gets put onto the queue and the code moves on without executing it.
  2. k=1; next loop iteration, call setTimeout(); callback function gets put onto the queue and the code moves on without executing it.
  3. k=2; next loop iteration, call setTimeout(); callback function gets put onto the queue and the code moves on without executing it.
  4. k=3; fails the k<3 check and breaks out of the loop.
  5. Now it goes back and looks to see if anything is on the queue waiting to be executed, and finds all the callback functions.
  6. Executes the callback functions one by one, with the current value of k, which is 3.

Understanding this is key in JavaScript. As others have already stated, you need to pass the value of k into each callback (closure) to have access to the value at the time the callback was first put onto the stack.

(Code from other answers works perfectly):

for(var k=0; k<3; k++) {
    (function(k) {
        setTimeout(function() {
            console.log(k);
        },1000);
    })(k);
}

As for the 3635 and 3686 identifiers, that's actually something new I learned just now from the other answers! Thanks!

The first integer you're seeing is what's returned from the setTimeout.
It's a numeric pointer to the timeout that you can use in .clearTimeout()

Other than that, you'll have to use a closure to print the proper value.

for(var k=0; k<3; k++) {
    (function(k) {
        setTimeout(function() {
            console.log(k);
        },1000);
    })(k);
}

The four-digit numbers you are seeing are the return values of setTimeout, which is a numerical id that you could later use to cancel the timer.

The setTimeout method sets its argument to be called later, and continues the current function immediately. So your code quickly finishes its for loop, calling setTimeout three times. When that loop is done, k is 3.

Then, one second later, the timeout fires, and logs k (3) to the console.

I believe the reason you only see 3 output once is that setTimeout will only set a single timeout for a given function. You're passing exactly the same function three times. I don't see this behavior specified in the documentation, though, so it may be implementation defined.

I suspect that what you want is to print the three different values of k after one second. To do that, you actually need to make a different variable, the value of which doesn't change. You can do that with an extra function that returns a function:

var makefunc = function (k2) {
    return function() { console.log(k2); };
}
for(var k=0; k<3; k++) {
    setTimeout(makefunc(k),1000);
}

Every time makefunc is called, it creates a new variable (named k2 here, but actually this would work just as well if that variable was named k). That variable stays alive until the console.log call one second later.

A more compact way of writing the same thing:

for(var k=0; k<3; k++) {
    setTimeout(function(k) { return function() { console.log(k); }; }(k), 1000);
}

3635 and 3686 are identifiers of setTimeout function , they are used to clear timeout if necessary. To print 0,1,2 use next code

for(var k=0; k<3; k++) {
(function(k){
setTimeout(function() { console.log(k); },1000);
})(k);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top