Multiple asynchronous jQuery operations such as .ajax() and animations can be sequenced (chained) using the accompanying patterns which make use of jQuery Deferred techniques rather than the less extensible and non-ideal recursive call techniques.
There are three usage patterns, each implemented herein and tested using the supplied data for .ajax() transactions...
- Sequential in series using pre-defined data;
- Sequential in series, feeding each request using the response from previous request;
- Parallel request using pre-defined data, where responses are sequenced.
Typical examples of (1) might be a sequence of set animations that must be performed one after the other, or a set sequence of calls to external web services.
An example of (2) might be loading parts of a web page from web services where each part depends upon the data contained within the previous part.
Displaying several external web pages onto a single web page might be an example of scenario (3). The AJAX transactions to perform this could be performed serially one after the other as per scenario (1) but this would take longer. Consider that if ten page parts must be loaded and they each take around half a second to load, it would take 5 seconds to load them one after the other, whereas if they were loaded in parallel the overall operation would take only around half a second in total.
Note that all my patterns continue to cycle regardless of whether an operation fails. Each pattern does provide a place where code can be written to optionally stop the sequence upon a failure, but in my experience this is not a very common requirement for multiple sequential asynchronous operations.
By default, JavaScript/jQuery perform .ajax() operations in parallel. If written simply one line after the other, AJAX operations will be kicked off straight away one after the other up to the limits of the client and server capabilities - often either 2 or 8 simultaneous AJAX transactions - when the situation is said to become "throttled" and further transactions are automatically fed through as soon as one of the connection sockets becomes free.
When sending 'n' AJAX requests in parallel, it cannot be assumed that the responses will be received in the same order - because responses will have varying lengths of data, the server may take differing times to construct the response, and usually errors will be reported back more quickly.
Sometimes the order of received responses does not matter and my special sequencing pattern will not be required. For example, if each of one hundred text fields on a web page need to be populated by .ajax() calls, then as long as the context of each .ajax() call is mapped to the relevant field then it doesn't actually matter in what order they are conducted.
If the responses from parallel requests do need to be reliably sequenced, then a developer's gut instinct is to fire off all transactions (let's say 100 transactions), and wait until they #all# complete. This is wasteful because if, say, the infrastructure only allows 8 simultaneous transactions, the "wait for all responses" algorithm would be waiting for around thirteen end-to-end request-response time periods before any responses are acted upon.
My special parallel sequencing pattern kicks off all requests straight away but doesn't wait for all to complete before starting to process the responses. My algorithm will process the responses as soon as they arrive back, but only in the required sequence.
My requests object allows callbacks to be declared at design time along with the request data and options, using a neat two-layer object pattern.
Each top layer object represents the data and options for each request such as the url, data, type, contentType, etc, and the allowed callbacks: success, error and complete. A "seq" object is nested into each request providing an extremely simple means to define the callbacks to be acted upon when the response has been received and sequenced - acting upon .done, .fail and .always once the response has been received and successfully sequenced. All callbacks within this model can be arrays of functions to allow multiple actions to be performed for each event type. The format is self-evident by looking at the example "requests" object included within this Gist.