Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery scope or race condition in AJAX/getJSON

I have a piece of jQuery code which invokes several getJSON() calls in quick succession:

var table = $("table#output");
for (var i in items) {
    var thisItem = items[i];
    $.getJSON("myService", { "itemID": thisItem }, function(json) {
        var str = "<tr>";
        str += "<td>" + thisItem + "</td>";
        str += "<td>" + json.someMember + "</td>";
        str += "</tr>";
        table.append(str);
    });
}

When I run this against a laggy server, the table gets populated with the expected json.someMember values (they arrive out of order: I don't mind that), but the thisItem column is populated with an unpredictable mixture of values from various iterations.

I'm assuming this is something to do with scope and timing - the callback function is reading thisItem from a wider scope? Am I right? How do I prevent this?

My current workaround is for the JSON service to return a copy of its inputs - which is unsatisfying to say the least.

like image 459
slim Avatar asked Jan 14 '09 22:01

slim


2 Answers

Seems like a scoping issue due to the loop. Try this:

var table = $("table#output");
for (var i in items) {
    var thisItem = items[i];
    $.getJSON("myService", { "itemID": thisItem }, (function(thisItem) {
        return function(json) {
            var str = "<tr>";
            str += "<td>" + thisItem + "</td>";
            str += "<td>" + json.someMember + "</td>";
            str += "</tr>";
            table.append(str);
        }
    })(thisItem));
}

Edit: all I did was scope thisItem to the $.getJSON callback.

like image 129
Crescent Fresh Avatar answered Oct 15 '22 01:10

Crescent Fresh


Javascript does not use block for scope. Scope is only based on functions.

If you want a new scope, you have to declare a new internal function and run it immediately, this is the only way to create a new scope in Javascript.

var table = $("table#output");
for( var i in items ) 
{
    (function(){
        var thisItem = items[i];
        $.getJSON("myService", { "itemID": thisItem }, function(json) 
        {
            var str = "<tr>";
            str += "<td>" + thisItem + "</td>";
            str += "<td>" + json.someMember + "</td>";
            str += "</tr>";
            table.append(str);
        });
    })();
}
like image 36
Vincent Robert Avatar answered Oct 15 '22 02:10

Vincent Robert