alex2k8
alex2k8

Reputation: 43214

JavaScript variable binding and loop

Consider such loop:

for(var it = 0; it < 2; it++)
{
    setTimeout(function() {
        alert(it);
    }, 1);
}

The output is:

=> 2
=> 2

I would like it to be: 0, 1. I see two ways to fix it:

Solution # 1.

This one based on the fact that we can pass data to setTimeout.

for(var it = 0; it < 2; it++)
{
    setTimeout(function(data) {
        alert(data);
    }, 1, it);
}

Solution # 2.

function foo(data)
{
    setTimeout(function() {
        alert(data);
    }, 1);
}

for(var it = 0; it < 2; it++)
{
    foo(it);
}

Are there any other alternatives?

Upvotes: 31

Views: 16424

Answers (4)

Eivind Eklund
Eivind Eklund

Reputation: 208

Similar to the other solutions, but in my opinion cleaner:

for (var it = 0; it < 2; it++) {
  // Capture the value of "it" for closure use
  (function(it) {
     setTimeout(function() {
       alert(it);
     }, 1);
  // End variable captured code
  })(it)
}

This keeps the same variable name for the capture, and does it for the entire loop, separating that from the logic of the timeout setup. If you want to add more logic inside the block, you can trivially do that.

The only thing I don't like about the solution is the repeat of "it" at the end.

Upvotes: 1

tbondwilkinson
tbondwilkinson

Reputation: 1087

With the let keyword you can get around this completely:

for(let it = 0; it < 2; it++)
{
    setTimeout(function() {
        alert(it);
    }, 1);
}

Upvotes: 21

Russ Cam
Russ Cam

Reputation: 125498

Not really anything more than the two ways that you have proposed, but here's another

for(var it = 0; it < 2; it++)
{
    (function() {
        var m = it;   
        setTimeout(function() {
            alert(m);
        }, 1);
    })(); 
}

Essentially, you need to capture the variable value in a closure. This method uses an immediately invoked anonymous function to capture the outer variable value it in a local variable m.

Here's a Working Demo to play with. add /edit to the URL to see the code

Upvotes: 45

Prahlad
Prahlad

Reputation: 364

Similar to above solution but self invoking inside of setTimeout function

for(var it = 0; it < 2; it++)
{
    setTimeout(function(cur) {
        return function(){
           alert(cur);
        };
     }(it), 1);
 }

Upvotes: 2

Related Questions