Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery AJAX: collecting multiple asynchronous results

I'm running a validation on form data, such that when somebody hits the submit button it checks the form's contents first. A number of fields (could be zero, could be more than one, depending on the page) have things like unique codes which require checking with the server first. I'm running an asynchronous request to the server, which simply responds 'ok' or 'taken' for each field. If any of the fields have errors, the page doesn't submit.

On the one hand, this should be asynchronous: it should continue validating the rest of the form fields while that request is off processing, including firing off other requests. If the call is made synchronously, then it visibly slows down feedback on later fields.

On the other hand, I want to make sure all the validation requests have returned (or timed out) before responding yes or no to the validate() method, and either allowing the submit to proceed or not. So if there are two fields that need validating then the validate() method should fire off those two AJAX requests, process the rest of the fields, and then wait until those two requests have returned before finally returning.

I can probably achieve this with some ugly homebaked solution (probably involving an array of random ids representing the requests in progress or something), but before I do is there any built-in function, plugin or standard technique I should be using instead?


Clarification

What I think I need is to make the code wait for the result from one or more asynchronous requests before proceeding with a method. This isn't the same as a callback, because the result of the method depends on the result of the requests. It isn't the same as a synchronous request because there may be more than one of them.

I'm using this to check the form before submitting:

$("form").submit(function () {
    return validate($(this));
});

If the validate() method returns false, then the form doesn't submit. validate() hilights any fields that aren't accepted. For normal fields, validate() looks something like this (vastly simplified version, without the feedback):

function validate(form) {
    resetWarnings(form);
    var ok = true;

    //  required fields
    form.find("input.required").each(function () {
        var field = $(this);
        if (field.val() === "") {
            ok = false;
        }
        return this; // meaningless in this case but a good habit to keep
    });

    //  fields that matches a pattern
    form.find("input.pattern").each(function () {
        var field = $(this);
        var pattern = field.data("pattern");
        if (!field.val().match(pattern)) {
            ok = false;
        }
        return this;
    });

    //  various other rules for other sorts of field
    ...

    return ok;
}

For AJAX fields it's more like this:

    form.find("input.lookup").each(function () {
        var field = $(this);
        var url = field.data("lookup");
        $.ajax({
            url: url,
            data: { code: field.val() },
            success: function (result) {
                if (result === "taken") {
                    ok = false;
                }
            }
        }
    });

But of course, validate() has already finished before the success method is called. So if I can make it wait until the ajax has been completed (either success or error) before returning, I can get the right result and stop the form being submitted. And if I made the ajax synchronous, then the entire method stops until it's done, which is wrong.


Further thought

Given Javascript's threading model (which is to say none at all), is what I'm asking for technically impossible?

like image 456
Marcus Downing Avatar asked Nov 04 '09 17:11

Marcus Downing


People also ask

Can we execute run multiple Ajax request simultaneously in jQuery if yes then how?

To iterate through the response, there is a callback function attached to it. This callback function gets executed once both the Ajax requests are finished. In case where multiple Deferred objects are passed to $. when() , it takes the response returned by both calls, and constructs a new promise object.

Is jQuery Ajax asynchronous?

The ajax() method is used to perform an AJAX (asynchronous HTTP) request. All jQuery AJAX methods use the ajax() method. This method is mostly used for requests where the other methods cannot be used.

Is Ajax async false deprecated?

As of jQuery 1.8, the use of async: false with jqXHR ( $.Deferred ) is deprecated; you must use the success/error/complete callback options instead of the corresponding methods of the jqXHR object such as jqXHR.done() .

Can Ajax requests be made synchronous?

AJAX can access the server both synchronously and asynchronously: Synchronously, in which the script stops and waits for the server to send back a reply before continuing. Asynchronously, in which the script allows the page to continue to be processed and handles the reply if and when it arrives.


2 Answers

With jQuery 1.5+ the best way to collect result data from multiple asynchronous requests is to use Deferred objects which represent these requests with their states, and wrapper function $.when(...).then( allRequestsCompleteCallback(), someRequestFailedCallback() )

That helps to avoid using of multiple nested callback functions making your code much cleaner and easier to maintain.

JavaScript / jQuery example:

function ajax1() { return $.ajax({ url: 'server1.php' }) }
function ajax2() { return $.ajax({ url: 'server2.php' }) }

$.when( ajax1(), ajax2() ).then(
    function() {
        // ajax1 AND ajax2 succeeded
    },
    function() {
        // ajax1 OR ajax2 succeeded
    }
);

Check for more docs and examples here:

  • http://api.jquery.com/category/deferred-object/
  • http://api.jquery.com/jQuery.when
  • http://api.jquery.com/deferred.then
like image 181
Alexey Avatar answered Nov 15 '22 05:11

Alexey


If I understand you correctly, you are wanting to check that the values (1..*) entered in a form are unique, once the user hits the submit button before performing the post on the form. If that is the case, I would create a JSON array of fields and values and send it all at once - so you are only waiting for one response. This should be quite easy using the jQuery 'each' function and using jQuery ajax.

You can then check the single result before posting the form.

It probably goes without saying, but make sure you repeat the same validation serverside once you have posted to avoid race conditions with other users.

You could validate individual fields as the user completes the form, and store the result, but this would be signficantly more chatty (for scalability reasons you should consider bandwidth) and you expose yourself to a greater chance of failing server side validation.

like image 44
Mike Avatar answered Nov 15 '22 05:11

Mike