3

I recently asked a question about the behaviour of jquery deferred in a for loop. Link here

I received a working answer but I don't understand why it works.

If I have the following code:

function update(callbacks) {
    return $.Deferred(function(dfr) {
        setTimeout(function() {
            callbacks.success()
        }, 1000);
        dfr.resolve();
    }).promise();
}

function updateElements(deferreds) {
    for (var i = 0; i < 5; i++) {
        (function() {
            var index = i;
            deferreds.push(update({
                success: function() {
                    alert(index);
                }
            }));
        })();
    }
};

(function() {
    var deffereds = [];
    updateElements(deffereds);
    $.when.apply($, deffereds).then(function() {}, function() {});
})();​

It returns 5 alert windows with the values 0 through to 4. If I change the updateElements method to:

function updateElements(deferreds) {
    for (var i = 0; i < 5; i++) {
        var index = i;
        deferreds.push(update({
            success: function() {
                alert(index);
            }
        }));
    }
};

It returns 5 alert windows with the value 4 only. Could someone please explain this behaviour? I'm struggling to understand where the difference comes about.

Thanks!

2
  • 2
    The loop runs in a few milliseconds, updating the number, and since deferred is asynchronous and waits, by the time it shows the number, the variable has changed and the loop has long since completed. By wrapping it in an anonymous function the value of the index variable keeps and does'nt get updated, as it is a local variable set on each iteration. Commented Nov 15, 2012 at 8:51
  • possible duplicate of Javascript closure inside loops - simple practical example Commented Nov 15, 2012 at 11:48

1 Answer 1

3

The reason that it does that is because you have closed over a loop with

(function() {
        var index = i;
        deferreds.push(update({
            success: function() {
                alert(index);
            }
        }));
})();

This self executing block turns into a static value because it has no external values passed in. As in the answer you linked, you need to pass that value in. Note the key difference where the value is given at the end of the IEFE (immediately executed function expression). Sorry for the caps, but this needs emphasis.

(function(VALUE_ACCEPTED){
  //VALUE_ACCEPTED accepts the passed value of VALUE_PASSED
})(VALUE_PASSED)

So that your code becomes this:

function updateElements(deferreds) {
for (var i = 0; i < 5; i++) {
    (function(valueAccepted) { // valueAccepted = the passed in value from i
        var index = valueAccepted;
        deferreds.push(update({
            success: function() {
                alert(index);
            }
        }));
    })(i); // pass in i to valueAccepted
 }
};
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.