1
for (var i = 0; i < 5; i++) {
    with (x = new XMLHttpRequest()) open("GET","d.php?id=" + i), send(null), onreadystatechange = function() {
       if (x.readyState == 4 && x.status == 200) alert(i);
    }
}

Now I want each time when readyState = 4, it should alert correct value of i for which URL was called. Currently, it alert only for once and output alert is 5

David G
  • 94,763
  • 41
  • 167
  • 253
Wasim A.
  • 9,660
  • 22
  • 90
  • 120

4 Answers4

4

If you want to use with to retain i, you'd either need to add it to an object that also references the xhr object:

for(var i=0;i<5;i++){
    with({i:i, xhr:new XMLHttpRequest()}) {
        xhr.open("GET","d.php?id=" + i);
        xhr.send(null);
        xhr.onreadystatechange=function(){
            if (xhr.readyState == 4 && xhr.status == 200)
                alert(i);
        }
    }
} 

Or you'd need to create the xhr outside the with and add i to it.

var xhr;
for(var i=0;i<5;i++){ 
    (xhr = new XMLHttpRequest()).i = i;
    with(xhr) {
        open("GET","d.php?id=" + i);
        send(null);
        onreadystatechange=function(){
            if (readyState == 4 && status == 200)
                alert(i);
        }
    }
} 

But if you want a proper, future-proof solution, make the handler in a variable scope that provides the variables needed for the handler.

function doRequest(i, xhr) {
    xhr.open("GET","d.php?id=" + i);
    xhr.send(null);
    xhr.onreadystatechange=function(){
        if (xhr.readyState == 4 && xhr.status == 200)
            alert(i);
    }
}

And call it like this:

for(var i=0;i<5;i++){
    doRequest(i, new XMLHttpRequest());
} 

Or if you insist upon inlining the function as some do, you could do it like this:

for(var i=0;i<5;i++){
    (function (i, xhr) {
        xhr.open("GET","d.php?id=" + i);
        xhr.send(null);
        xhr.onreadystatechange=function(){
            if (xhr.readyState == 4 && xhr.status == 200)
                alert(i);
        }
    }(i, new XMLHttpRequest());
} 
I Hate Lazy
  • 47,415
  • 13
  • 86
  • 77
  • Hi, your answer helped solve an issue I was having, but I'm not sure why - was hoping you could explain -- I was initially had all my code for `doRequest` in my for loop, but only the last request would go through. I was creating a new request inside the loop like this `var request = new XMLHttpRequest()` . Once I separated this out into another function and passed the required variables, it all worked. Why is this? Haven't encountered a situation like this before. Thanks for the help! – sooks Apr 07 '16 at 10:55
1

That's because the closure captures the i variable itself, not just the current value. I'd personally do something like:

for(var i = 0; i < 5; i ++) (function(i) {
        [..] alert(i) [..]
})(i);
James M
  • 18,506
  • 3
  • 48
  • 56
1

Just updating James code to suit your needs

for (var i = 0; i < 5; i++) {
    with (x = new XMLHttpRequest()) open("GET","d.php?id=" + i), send(null), onreadystatechange = (function(i, x) {
        return function () {
        if (x.readyState == 4 && x.status == 200) alert(i);
       }
    })(i, x)
}

Hope this helps

Updated it for x, this should work

Sudesh
  • 1,129
  • 3
  • 14
  • 29
0

This is how i sloved it

var x = new Array();
for (var i = 0; i < 5; i++) {
    with (x[i] = new XMLHttpRequest()) open("GET","d.php?id=" + i), send(null), onreadystatechange = (function(i) {
        return function () {
        if (x[i].readyState == 4 && x[i].status == 404) alert(i);
       }
    })(i)
}

but i am interested more in using closure to solve my problem, how can i use closure to solve my issue.

Wasim A.
  • 9,660
  • 22
  • 90
  • 120
  • If you're going to use a closure, you might as well take advantage of the variable scope, and get rid of `with`. I updated my answer with a proper closure example. – I Hate Lazy Oct 28 '12 at 19:03