0

I have doing a website with javascript but I can't do that I want.

I have this

<script type="text/javascript">
res = new Array();

var fun1 = function () {
var control = $.Deferred();
for (i=0;i<5;i++) {
    $.get("URL", function(data){
        res[i]=data;
        console.log ("i is: " + i + "and res is: " + res);
    });
    }
}

setTimeout(function () {
control.resolve();
}, 3000);

var show = function () {
console.log("Res finally is: " + res);
}

fun1().done(show);  
</script>

I want to do a $.get with 5 or more different URL (I have a param in the URL) but I can't do it. res[i] is always the last element in the array (in this case is always res[5]=data and I want to fill the complete array, from 0 to 15.

First console.log always show

i is:  5 and res is: ,,,,,20
i is:  5 and res is: ,,,,,10
...
i is:  5 and res is: ,,,,,38

and the second console.log always return the last

Res finally is ,,,,,38

How can I do it correctly?

Thanks!

BGG
  • 23
  • 1
  • 6
  • http://stackoverflow.com/questions/1451009/javascript-infamous-loop-issue – epascarello Jan 20 '15 at 16:15
  • This question is really two questions. It's best in general to keep questions distinct, to post them separately. epascarello's duplicate answers one of your two questions; the other relates to combining promises (and there's almost certainly an answer here about that, too, but we can only close a question as a dupe of *one* other question...). – T.J. Crowder Jan 20 '15 at 16:21

1 Answers1

1

The main issue with i is that your callbacks close over the variable i, not its value as of when the function was created. So they all see i = 5.

I'm not quite understanding why you're repeating the get five times, but if you want to, you have to give the callbacks something else to close over (or use res.push(...) rather than res[i] = ..., but I assume you have a reason for the latter).

You can do that using a builder:

// PARTIAL solution, see below
var fun1 = function () {
    var control = $.Deferred();
    for (i=0;i<5;i++) {
        $.get("URL", buildHandler(i));
    }

    function buildHandler(index) {
        return function(data){
            res[index]=data;
            console.log ("index is: " + index + "and res is: " + res);
        };
    }
};

Another way to do a builder is to use Function#bind (ES5+, but easily polyfilled), but you'd create more functions than you need; the above is more efficient in this case (not that it likely matters).

Then, to have fun1 return something useful, you have it return a promise (you seem to be on your way to that, based on your control variable), and then fulfill the promise when all of the gets are done ($.when is useful for that):

var fun1 = function () {
    var control = $.Deferred();
    var promises = [];
    for (i=0;i<5;i++) {
        promises.push($.get("URL", buildHandler(i)));
    }
    $.when.apply($, promises).then(function() {
        control.resolve(); // Or .resolveWith(), passing in something useful
    });

    return control.promise();

    function buildHandler(index) {
        return function(data){
            res[index]=data;
            console.log ("index is: " + index + "and res is: " + res);
        };
    }
};

All of this assumes res is in scope for this code; you haven't shown where it comes from.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875