views:

150

answers:

5

Hello. 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.

A: 

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);
    }
});
Joel Potter
A: 

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.

Tatu Ulmanen
There's no reason he needs to do asynchronous calls to achieve what he wants.
Rob Van Dam
That is true, but that was just to demonstrate how he would have to do it so that the step_name wouldn't change in the mean time.
Tatu Ulmanen
I meant 'synchronous'. Actually, what I really meant was, your answer is completely wrong. It has nothing to do with the fact that the calls are asynchronous.
Rob Van Dam
+2  A: 

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
}
Rob Van Dam
A: 

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.

CalebD
A: 

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.

Jason