JavaScript variable binding and loop

15,337

Solution 1

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

Solution 2

With the let keyword you can get around this completely:

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

Solution 3

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);
 }

Solution 4

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.

Share:
15,337
Hadeel Fouad
Author by

Hadeel Fouad

e-mail: alex2k8.stackoverflow [at] gmail [dot] com

Updated on June 03, 2022

Comments

  • Hadeel Fouad
    Hadeel Fouad almost 2 years

    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?