1

Here is the code that loads all five images successfully:

for(i=0; i<5; i++) {
  (function() {
    var oReq = new XMLHttpRequest();
    var r = "images/"+i+".jpg";
    oReq.open("GET",r, true);
    oReq.responseType = "arraybuffer";
    oReq.send();

    oReq.onload = function(oEvent) {
        var blob = new Blob([oReq.response], {type: "image/jpg"});
        var x = window.URL.createObjectURL(blob);
        var img = new Image(); 
        img.src = x;
        img.width = 100;
        $("#someDiv").append(img);
        };
    })();
}

Here is the code that loads only the last image:

  for(i=0; i<5; i++) {
    var oReq = new XMLHttpRequest();
    var r = "images/"+i+".jpg";
    oReq.open("GET",r, true);
    oReq.responseType = "arraybuffer";
    oReq.send();

    oReq.onload = function(oEvent) {
        var blob = new Blob([oReq.response], {type: "image/jpg"});
        var x = window.URL.createObjectURL(blob);
        var img = new Image(); 
        img.src = x;
        img.width = 100;
        $("#someDiv").append(img);
    };
}

Why does the callback to an asynchronous function in a for loop only work when the code is placed in a function rather than placed directly inside of the loop?

Headache
  • 239
  • 2
  • 8
  • 3
    Because calling a function creates a new scope, giving each callback "its own" `oReq` variable. See [JavaScript closure inside loops – simple practical example](http://stackoverflow.com/q/750486/218196). Without it, `oReq` will always refer to the value of the last iteration, hence `new Blob([oReq.response], {type: "image/jpg"});` always receives the response from the last request. – Felix Kling Dec 28 '16 at 19:07
  • Note in ES6 you could get the desired behavior by declaring all your variables within the loop using `let` instead of `var`. That works for the same reason - you close over a new variable each time rather than capturing the same one in each iteration. – PMV Dec 28 '16 at 19:16
  • 1
    its because of "Scope of variables"..when you put your variables inside function (as in your case) each var has its own scope (your functions in loop) but when you use it directly its the same variable that's get overridden with new value..result you have final value as result – RohitS Dec 28 '16 at 19:21

1 Answers1

2

In the first case, there are as many oReq variables as there are iterations of the loop, because each time the wrapping function is invoked ((function() {...})()), it creates a new scope that includes a new oReq variable.

In the second case, there is a single oReq variable that is reused and set to a new value with each iteration of the loop.

This is significant because it determines the value that oReq refers to when new Blob([oReq.response], ...); is executed in the onload handler. In the first case, it refers to the oReq local to the anonymous wrapping function, so each onload function refers to a unique oReq. In the second case, all onload callbacks refer to a single oReq value, which holds the single final value stored in oReq

apsillers
  • 112,806
  • 17
  • 235
  • 239