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
  • 90,891
  • 40
  • 158
  • 247
Wasim A.
  • 9,201
  • 21
  • 89
  • 117

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
  • 45,169
  • 12
  • 84
  • 76
  • 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 McLaughlin
  • 18,774
  • 3
  • 47
  • 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,039
  • 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,201
  • 21
  • 89
  • 117
  • 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