Dom M.
Dom M.

Reputation: 3869

jQuery: In $.post call, callback is not being processed until the end

I have a $.post function inside of a loop. All it does it call a php function to validate an input. If the input validates, it returns "true" (which I tested and works). At this point I use a callback to do some processing and it is not working.

For example if i am looping over three items, the callback function processes the third item three times instead of each one.

Here is my relevant code:

for (step in steps) {
            var step_name = steps[step];
            // grab input value
            var step_answer = escape($("#" + step_name).val());
            if (step_answer != "") {
                // check to see if answer validates
                console.log(step_name) // THIS SHOWS CORRECT VALUES: 1, 2, and 3
                $.post("utility.php", {
                    utility: "validateAnswer",
                    step: step_name,
                    answer: step_answer
                },
                function(data) {
                    // if validation suceeds..
                    if (data == "true") {
                        console.log(step_name); // THIS SHOWS WRONG VALUES: 3, 3, and 3
                        correct_steps.push(step_name);
                    }
                });
            } 
        }

Any ideas? Thanks.

Upvotes: 1

Views: 369

Answers (5)

Rob Van Dam
Rob Van Dam

Reputation: 7960

This is a problem with the way javascript does closures. Basically, when you create a variable at the top level (like you are doing), it actually just creates window.step_name which is actually a global variable and therefore the value doesn't get encapsulated in the callback.

However, if you create a variable inside a function, this does not happen. So try wrapping your code in a function and see if it works.

Simple way:

(function() {
    // code here
})();

If that works you might want to consider putting your code in a named function for easier maintenance later.

Alternatively, you could just use jquery's $.each:

$.each(steps, function() {
    var step_name = this;
    // code from inside loop here
}

Upvotes: 2

Tatu Ulmanen
Tatu Ulmanen

Reputation: 124768

That is because the step_name is declared in the for loop, and the for loop gets to the end before the first POST request is completed. That is, when the function inside the $.post is executed, the loop has looped through and step_name equals to 3. That's the nature of asynchronous calls, you can correct that if you wish by using an $.ajax call and setting async: false:

for (step in steps) {
    var step_name = steps[step];
    // grab input value
    var step_answer = escape($("#" + step_name).val());
    if (step_answer != "") {
        // check to see if answer validates
        console.log(step_name) // THIS SHOWS CORRECT VALUES: 1, 2, and 3
        $.ajax(
            url: "utility.php",
            data: {
                utility: "validateAnswer",
                step: step_name,
                answer: step_answer
            },
            async: false,
            success: function(data) {
                // if validation suceeds..
                if (data == "true") {
                    console.log(step_name); // THIS SHOWS CORRECT VALUES: 1, 2, and 3
                    correct_steps.push(step_name);
                }
           });
       } 
   }
}

There's no need to do it like this though, "utility.php" will get the right values regardless how you do it. It would be wiser to log out the response from the PHP script to get the correct result.

Upvotes: 0

Jason
Jason

Reputation: 792

The only thing I can think of is that your ajax call isn't complete until your loop is already completed and the step variable is incremented to 3.

Upvotes: 0

CalebD
CalebD

Reputation: 5022

I'm guessing what you're seeing is due to using a closure within a loop. Maybe try:

            $.post("utility.php", {
                utility: "validateAnswer",
                step: step_name,
                answer: step_answer
            },
            function(data) {
                // if validation suceeds..
                if (data == "true") {
                    console.log(this.data.step);
                    correct_steps.push(this.data.answer);
                }
            });

In the $.post callback, this is the AJAX request object.

Upvotes: 0

Joel
Joel

Reputation: 19358

Since step_name is a local parameter outside the post() call, it is being modified (to 3) before the async callback fires.

Can you try passing the step_name back down on the response? Maybe as a part of a json object like the following.

function(data) {
    // if validation suceeds..
    // data example: { valid: "true", step: 1 }
    if (data.valid == "true") {
        console.log(data.step);
        correct_steps.push(data.step);
    }
});

Upvotes: 0

Related Questions