Sequencing Ajax Requests

Sequencing ajax requests

jQuery 1.5+

I developed an $.ajaxQueue() plugin that uses the $.Deferred, .queue(), and $.ajax() to also pass back a promise that is resolved when the request completes.

/*
* jQuery.ajaxQueue - A queue for ajax requests
*
* (c) 2011 Corey Frang
* Dual licensed under the MIT and GPL licenses.
*
* Requires jQuery 1.5+
*/
(function($) {

// jQuery on an empty object, we are going to use this as our Queue
var ajaxQueue = $({});

$.ajaxQueue = function( ajaxOpts ) {
var jqXHR,
dfd = $.Deferred(),
promise = dfd.promise();

// queue our ajax request
ajaxQueue.queue( doRequest );

// add the abort method
promise.abort = function( statusText ) {

// proxy abort to the jqXHR if it is active
if ( jqXHR ) {
return jqXHR.abort( statusText );
}

// if there wasn't already a jqXHR we need to remove from queue
var queue = ajaxQueue.queue(),
index = $.inArray( doRequest, queue );

if ( index > -1 ) {
queue.splice( index, 1 );
}

// and then reject the deferred
dfd.rejectWith( ajaxOpts.context || ajaxOpts,
[ promise, statusText, "" ] );

return promise;
};

// run the actual query
function doRequest( next ) {
jqXHR = $.ajax( ajaxOpts )
.done( dfd.resolve )
.fail( dfd.reject )
.then( next, next );
}

return promise;
};

})(jQuery);

jQuery 1.4

If you're using jQuery 1.4, you can utilize the animation queue on an empty object to create your own "queue" for your ajax requests for the elements.

You can even factor this into your own $.ajax() replacement. This plugin $.ajaxQueue() uses the standard 'fx' queue for jQuery, which will auto-start the first added element if the queue isn't already running.

(function($) {
// jQuery on an empty object, we are going to use this as our Queue
var ajaxQueue = $({});

$.ajaxQueue = function(ajaxOpts) {
// hold the original complete function
var oldComplete = ajaxOpts.complete;

// queue our ajax request
ajaxQueue.queue(function(next) {

// create a complete callback to fire the next event in the queue
ajaxOpts.complete = function() {
// fire the original complete if it was there
if (oldComplete) oldComplete.apply(this, arguments);

next(); // run the next query in the queue
};

// run the query
$.ajax(ajaxOpts);
});
};

})(jQuery);

Example Usage

So, we have a <ul id="items"> which has some <li> that we want to copy (using ajax!) to the <ul id="output">

// get each item we want to copy
$("#items li").each(function(idx) {

// queue up an ajax request
$.ajaxQueue({
url: '/echo/html/',
data: {html : "["+idx+"] "+$(this).html()},
type: 'POST',
success: function(data) {
// Write to #output
$("#output").append($("<li>", { html: data }));
}
});
});

jsfiddle demonstration - 1.4 version

How to make all AJAX calls sequential?

There's a much better way to do this than using synchronous ajax calls. Jquery ajax returns a deferred so you can just use pipe chaining to make sure that each ajax call finishes before the next runs. Here's a working example with a more in depth example you can play with on jsfiddle.

// How to force async functions to execute sequentially 
// by using deferred pipe chaining.

// The master deferred.
var dfd = $.Deferred(), // Master deferred
dfdNext = dfd; // Next deferred in the chain
x = 0, // Loop index
values = [],

// Simulates $.ajax, but with predictable behaviour.
// You only need to understand that higher 'value' param
// will finish earlier.
simulateAjax = function (value) {
var dfdAjax = $.Deferred();

setTimeout(
function () {
dfdAjax.resolve(value);
},
1000 - (value * 100)
);

return dfdAjax.promise();
},

// This would be a user function that makes an ajax request.
// In normal code you'd be using $.ajax instead of simulateAjax.
requestAjax = function (value) {
return simulateAjax(value);
};

// Start the pipe chain. You should be able to do
// this anywhere in the program, even
// at the end,and it should still give the same results.
dfd.resolve();

// Deferred pipe chaining.
// What you want to note here is that an new
// ajax call will not start until the previous
// ajax call is completely finished.
for (x = 1; x <= 4; x++) {

values.push(x);

dfdNext = dfdNext.pipe(function () {
var value = values.shift();
return requestAjax(value).
done(function(response) {
// Process the response here.

});

});

}

Some people have commented they have no clue what the code does. In order to understand it, you first need to understand javascript promises. I am pretty sure promises are soon to be a native javascript language feature, so that should give you a good incentive to learn.

Run two functions with Ajax calls sequentially

Because the ajax calls are asynchronous, you will need to manually sequence the ajax calls if you want the second one not to start until the first one is done.

Promises are uniquely suited for serializing asynchronous operations and they can make it quite simple. Fortunately, jQuery has promises built in and every Ajax operation already returns a promise that can be used for this:

$.ajax(...).then(function(result1) {
// make 2nd ajax call when the first has completed
return $.ajax(...);
}).then(function(result2) {
// success of both ajax calls here
}, function(err) {
// error here
});

Or, if you make a() and b() both return the jQuery ajax promises from their ajax calls, then you can just do this:

a().then(b);

And, c() could just be:

function c() {
return a().then(b);
}

So, if you want to make a function call to contain both these Ajax calls without a() and b(), you should have it return a promise:

function c() {
return $.ajax(...).then(function(result1) {
// make 2nd ajax call when the first has completed
return $.ajax(...);
})
}

And, you can then call c() like this:

c().then(function(result) {
// success here
}, function(err) {
// error here
});

Here's an example of a function that returns an Ajax promise:

function test() {
return $.ajax("http://www.test.com/api/test");
}

Now, that you've added your actual ajax code, you can just add a return:

function a(){ 
return $.ajax({
url: "url_a",
type: "post",
dataType: "html",
cache: false
});
}

function b(){
return $.ajax({
url: "url_b",
type: "post",
dataType: "html",
cache: false
});
}

function c() {
return a().then(b);
}

Javascript asynchronous AJAX and recursive sequencing

You should probably use promises to chain requests and execution together.

var firstMethod = function() {
var promise = new Promise(function(resolve, reject){
setTimeout(function() {
console.log('first method completed');
resolve({data: '123'});
}, 2000);
});
return promise;
};

var secondMethod = function(someStuff) {
var promise = new Promise(function(resolve, reject){
setTimeout(function() {
console.log('second method completed');
resolve({newData: someStuff.data + ' some more data'});
}, 2000);
});
return promise;
};

var thirdMethod = function(someStuff) {
var promise = new Promise(function(resolve, reject){
setTimeout(function() {
console.log('third method completed');
resolve({result: someStuff.newData});
}, 3000);
});
return promise;
};

firstMethod()
.then(secondMethod)
.then(thirdMethod)

//... etc.

Here are some links to resources that might be helpful

https://html5hive.org/how-to-chain-javascript-promises/
http://www.html5rocks.com/en/tutorials/es6/promises/

https://github.com/kriskowal/q

How to call ajax functions in sequence?

I had a similar situation like this.

Solution
First make an array of selected checkboxes with values as array elements. Then make a function that check for that value and call its corresponding function.

 function callRequired(array) {
var required = array[0];
if (required === "f1") {
f1(array);
}
if (required === "f2") {
f2(array);
}
if (required === "f3") {
f3(array);
}
if (required === "f4") {
f4(array);
}
if (required === "f5") {
f5(array);
}
}

We are here checking the first element as required callback. Then removing it after each success. Simply remove that element and call callRequired(array) by removing the previous element as follows

function f1(array) {
jQuery.ajax({
url: "some/url",
success: function (response) {
array.splice(0, 1);
callRequired(array);
}
});
}
function f2(array) {
jQuery.ajax({
url: "some/url",
success: function (response) {
array.splice(0, 1);
callRequired(array);
}
});
}
function f3(array) {
jQuery.ajax({
url: "some/url",
success: function (response) {
array.splice(0, 1);
callRequired(array);
}
});
}
function f4(array) {
jQuery.ajax({
url: "some/url",
success: function (response) {
array.splice(0, 1);
callRequired(array);
}
});
}
function f5(array) {
jQuery.ajax({
url: "some/url",
success: function (response) {
array.splice(0, 1);
callRequired(array);
}
});
}

Hope this works. Thanks

How to process the results of a series of AJAX requests sequentially?

I would suggest using native Promises over jQuery's deferred objects. They are much more readable, easier to understand, native to the language, and have increasing browser support (with the exception of all IE versions). To account for browser support, use a polyfill like es6-promise and you'll be set. This article does a good job of explaining the basics of promises.

Print results one by one, slower overall

Check out the section of that article I linked to, titled "Parallelism and sequencing" because that really goes in depth on how this works. Essentially you need to create a promise generator function, some sort of mapping for how many ajax requests you need to make (in this case just an array that goes up by 20 each time), and a sequence variable that holds the previous promise that was looped through.

var totalRecsAvailable = 10; //generated elsewhere apparently
var recsPerPkt = 20;
var nextNdxRqst = 0;
var recordRanges = [];
var sequence = Promise.resolve(); //initialize to empty resolved promise

//this generates a promise
function getMyData(startPosition) {
return new Promise(function(resolve,reject){
$.ajax({
type: 'GET',
dataType: 'json',
url: 'eventSummaryData',
data: {StartNdxNum: startPosition, NumRecords: recsPerPkt}
success: function(response){resolve(response);},
error: function(response){reject(response);}
});
});
}

//build out array to inform our promises what records to pull & in which order
for (var i = 0; i < totalRecsAvailable; i++) {
recordRanges.push(nextNdxRqst);
nextNdxRqst += recsPerPkt;
}

//loop through record ranges, chain promises for each one
recordRanges.forEach(function(range) {
sequence = sequence.then(function() {
return getMyData(range); // return a new Promise
}).then(function(data) {
//do stuff with the data
addToHtmlTable(data.something);
}).catch(function(error) {
//something went wrong
console.log(error);
});
});

As outlined in that article, using reduce instead of a forEach is actually a bit better, but I thought this was more clear what was happening.

Wait until all processes resolve, faster overall

For slightly faster performance, you should use Promise.all(). This takes an iterable (like an array) of promises, runs those promises asynchronously, and then saves the results to an array in the order they were passed. If one of the promises fails, the whole thing will fail and give an error. This sounds exactly like what you need. For example, you could do something like this:

var recsPerPkt = 20;
var nextNdxRqst = 0;
var totalRecsAvailable = 10; //generated elsewhere apparently
var promises = [];

//this generates a promise
function getMyData(startPosition, recordsNumber) {
return new Promise(function(resolve,reject){
$.ajax({
type: 'GET',
dataType: 'json',
url: 'eventSummaryData',
data: {StartNdxNum: startPosition, NumRecords: recordsNumber}
success: function(response){resolve(response);},
error: function(response){reject(response);}
});
});
}

//create list of promises
for (var i = 0; i < totalRecsAvailable; i++) {
promises.push(getMyData(nextNdxRqst,recsPerPkt));
nextNdxRqst += recsPerPkt;
}

//This will run once all async operations have successfully finished
Promise.all(promises).then(
function(data){
//everything successful, handle data here
//data is array of results IN ORDER they were passed
buildTable(data);
},
function(data){
//something failed, handle error here
logoutError(data);
}
);

That should set you down the right path.

Handle Ajax responses in same order as Ajax requests

If you create a promise up front, you could keep chaining off of it to get your desired effect.

// ajax mockfunction sendRequest(v, delay) {    var def = $.Deferred();    setTimeout(function () {        def.resolve(v);    }, delay);    return def.promise();}
var ajaxPromise = $.Deferred().resolve().promise();var delay = 600; // will decrement this with each use to simulate later requests finishing sooner
// think of this as a click event handlerfunction doAction (btnName) { delay -= 100; var promise = sendRequest(btnName, delay); ajaxPromise = ajaxPromise.then(function () { return promise; }).done(function () { console.log(btnName); });}
doAction("1");doAction("2");doAction("3");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>


Related Topics



Leave a reply



Submit