2

I've found a posting(https://bbs.archlinux.org/viewtopic.php?id=58640) while I was googling for a similar problem as the poster's.

He was trying to figure out the fix for the code below.

for(var i=0; i<3; i++) {
    req[i] = new XMLHttpRequest();
    req[i].onreadystatechange = function() {

        if(req[i].readyState == 4 && req[i].status == 200) {
            URL = url[i];
            success();
        } 

    }
    req[i].open("GET", url[i], true);
    req[i].send(null);    
}

The solution was

for(var i=0; i<3; i++) {
    req[i] = new XMLHttpRequest();
    req[i].onreadystatechange = function(index) {
              return function() {
                    if(req[index].readyState == 4 && req[index].status == 200) {
                    URL = url[index];
                    success();
                   }
        };
    }(i);
    req[i].open("GET", url[i], true);
    req[i].send(null);    
}

and this was because of the scoping issue with req[i].

I tested the value of i inside onreadystatechange with a similar function, and it printed 2, 2, 2, instead of 0, 1, 2.

Apparently something is happening to i value there, but I am not sure what is happening.

nicael
  • 18,550
  • 13
  • 57
  • 90
Eric
  • 2,635
  • 6
  • 26
  • 66
  • Also, if anyone knows a better solution, please add it. Thanks. – Eric Apr 07 '14 at 19:07
  • 1
    This is an *Extremely* common issue for new developers to run into, but if you have no idea what is going on, it is almost impossible to even Google for. I voted to close the question because it is a duplicate, but the link there gives an explanation of what is up. In addition, you might find this helpful as well: http://www.mennovanslooten.nl/blog/post/62 – Jeremy J Starcher Apr 07 '14 at 19:48
  • You will need to test the value of `index`, not `i`! – Bergi May 11 '14 at 21:06

1 Answers1

0

This issue may be simplified to

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

it outputs 3, 3, 3 instead of 0, 1, 2.

The problem is that the i in the function is the same i used in the loop and, since the function is called after all iterations of the loop, it is always 3.

You can fix it using ES6 let:

for(var i=0; i<3; i++) {
    let j=i;
    setTimeout(function() {
        console.log(j);
    }, 0)
}

It works because let declares a block scope local variable. You can't do the same using var because then its scope is the current execution context.

Or you can use a wrapper function:

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

or

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

But then better move it outside the loop, to avoid recreating it at each iteration:

function f(j) {
    setTimeout(function() {
        console.log(j);
    }, 0)
}
for(var i=0; i<3; i++) {
    f(i);
}
Oriol
  • 274,082
  • 63
  • 437
  • 513