1

Working with an api and I need to one of the first responses alongside with the second response in order to serve up a new page. The problem I'm facing is that my variable $x is always set to whatever the last # is in the loop, ie 103 in this specific case. Here is my code:

$.ajax({
dataType: 'text',
type: 'post',
url: 'getAllMessages.php',
success: function(responseData) {
    var newString = responseData;
    var newerString = newString.substring(0, newString.length - 1);
    $newObject = jQuery.parseJSON(newerString);
    //console.log($newObject);
    for($x = 0; $x < $newObject.messages.length; $x++){
        $.ajax({
            data: {clientFolderId: $newObject.messages[$x].clientFolderId, messageId: $newObject.messages[$x].messageId},
            dataType: 'text',
            type: 'post',
            url: 'testapi.php',
            success: function(responseData2){
                //alert($x);
                var newString2 = responseData2;
                var newerString2 = newString2.substring(0, newString2.length - 1);
                $newObject2 = jQuery.parseJSON(newerString2);
                if($newObject2.statistics.delivered > 1000){
                    console.log($newObject.messages[$x]);
                    console.log($newObject2);
                }
            },
            error: function(responseData2){
                alert('failure in testapi.php');
            }
        });
    }
},
error: function(responseData) {
    alert('failure in getAllMessages.php');
}

});

Austin Munro
  • 333
  • 2
  • 3
  • 15
  • I hope $newObject.messages.length is short! Running ajax calls in a loop like this could completely kill your users browser.. Not to mention you have a limit in most browsers as to how many http requests you can make, see this: http://stackoverflow.com/questions/561046/how-many-concurrent-ajax-xmlhttprequest-requests-are-allowed-in-popular-browse – Matt Wolfe Apr 30 '12 at 18:46

2 Answers2

3

My intuition says nesting the Ajax call inside another functional scope (correction thanks to Matt) will resolve the unexpected behavior. I got burned by this already Object creation in loop broken; unrolled works

Also here, example #5: http://www.javascriptkit.com/javatutors/closures2.shtml

Following the pattern given by Engineer,

for($x = 0; $x < $newObject.messages.length; $x++){

   (function($x) {

    $.ajax({
        data: {clientFolderId: $newObject.messages[$x].clientFolderId, messageId: $newObject.messages[$x].messageId},
        dataType: 'text',
        type: 'post',
        url: 'testapi.php',
        success: function(responseData2){
            alert($x);
            var newString2 = responseData2;
            var newerString2 = newString2.substring(0, newString2.length - 1);
            $newObject2 = jQuery.parseJSON(newerString2);
            if($newObject2.statistics.delivered > 1000){
                console.log($newObject.messages[1]);
                console.log($newObject2);
            }
        },
        error: function(responseData2){
            alert('failure in testapi.php');
        }
    });

   })($x);
}
Community
  • 1
  • 1
Heitor Chang
  • 6,038
  • 2
  • 45
  • 65
  • the closure isn't what is fixing it, what is fixing it is nesting it inside another functional scope, and passing the desired value in as a parameter to said function. Closures have nothing to do with it. – Matt Apr 30 '12 at 18:48
  • To clarify, nesting functions like that will let you do/use closures, but in this particular case it works because it gives the variable a new scope, which doesn't get updated on the next iteration of the loop – Matt Apr 30 '12 at 18:52
  • @Matt Time for me to read more about the definition of closure. It seems it gets thrown around indiscriminately. You're right about a function giving a new scope; I believe from experience that defining a function elsewhere that performs the inner Ajax call should also give the expected result. – Heitor Chang Apr 30 '12 at 18:56
  • To sum up a closure, if you are using an inner/nested function that references a variable in the outer scope, that is a closure. If you are only using variables that are passed to the function, you are not using any closures. In the example above, if $newObject is *not* a global object, then it is being "closured" by your inner function, which is why you can use it inside the inner function, even though originally it was in the scope of the outer/wrapping function. – Matt Apr 30 '12 at 19:00
  • also, see http://stackoverflow.com/questions/111102/how-do-javascript-closures-work – Matt Apr 30 '12 at 19:03
  • @Matt Thanks for the additional info and links – Heitor Chang Apr 30 '12 at 19:18
0

What you're experiencing is closure. When the loop spins round, the value for $x is updated. However, when the ajax function comes to grab it - it's using a reference. So as you find, you end up with the last value.

Try and think more functional? What are you trying to do? Let's say you're trying to postMessage - wrap that in a function and pass in the message.

Your code will become easier to read, and you won't get your variables mangled.

I was about to throw out some code, but noticed something I wanted to clarify - you're using the index in both loops to get a single message from a messages array, yet the POST to testapi.php seems to be working on a single message? What kind of response is expected from that?

 Update: Created jsFiddle to Demo Problem

Here you go here's some code to help you out.

    function correctOutputPlox(id) {
     setTimeout(function() {
       $("#output").append("<li>" + id + "</li>");
     }, 500);   
    }

    function runNicely() {
        // same loop...
        for (var x = 0; x < 10; x++) {
            // but rather than use 'x' (which is going to change, we pass it's value into a function which doesn't have access to the original 'x' since it's in a different lexical scope.
            correctOutputPlox(x);
        }
    }

    function showProblem() {
        for (var x = 0; x < 10; x++) {
            setTimeout(function() {
                $("#output").append("<li>" + x + "</li>");
            }, 500);
        }
    }

    showProblem();
    runNicely();
Community
  • 1
  • 1
Rob Cooper
  • 28,567
  • 26
  • 103
  • 142