Search code examples
javascriptjquerygetjson

Inner $.getJSON won't go to second element in loop


array1 is ["orange","blue"].

$.getJSON("path1.json",function(array1){
    for (var i = array1.length - 1; i >= 0; i--) {
        var path2 = array1[i];
        console.log(path2);
        $.getJSON(path2,function(someObject){
            console.log("Inside the second $.getJSON function");
            console.log(path2);
        });
    }
});

The output looks like this.

"orange"
"blue"
"Inside the second $.getJSON function"
"blue"
"Inside the second $.getJSON function"
"blue"

Why isn't the output this?

"orange"
"Inside the second $.getJSON function"
"orange"
"blue"
"Inside the second $.getJSON function"
"blue"

Solution

  • There are two things going on:

    • $.getJSON() is partially asynchronous. The implication of this is that your callbacks happen asynchronously.
    • Variables declared with var are scoped to the function, not the block, and, while you can re-declare a variable in a given scope using var, doing so has no effect.

    When you combine these things, you end up with a situation where all iterations of the for loop are completed before any of the callbacks are called and so, by the time a callback happens, path2 has been updated several times. (Coincidentally, this doesn't actually effect the inner $.getJSON() call itself since path2 is passed in by value.)

    In the old days, we'd have to fix the scope of the value of path2 (usually via an IIFE) so it doesn't get overwritten before the callback is executed:

    $.getJSON("path1.json", function(array1){
        for (var i = array1.length - 1; i >= 0; i--) {
            var path2 = array1[i];
            console.log(path2);
            $.getJSON(path2,
                function(path2) {
                    return function(someObject){
                        console.log("Inside the second $.getJSON function");
                        console.log(path2);
                    };
                }(path2)
            );
        }
    });
    

    These days, we have let, which scopes variables to the block. for's block scope is created new on each iteration and that scope instance is bound to the callback each time the callback function is created, so the following works:

    $.getJSON("path1.json",function(array1){
        for (var i = array1.length - 1; i >= 0; i--) {
            let path2 = array1[i];
            console.log(path2);
            $.getJSON(path2, function(someObject){
                console.log("Inside the second $.getJSON function");
                console.log(path2);
            });
        }
    });