Multiple ajax requests problems and AjaxQueue as solution
For my app I had to load many import processes at once by Ajax requests, so I ran into some serious problems.
1. Session data was not available each second request
I used the database option for Sessions, and that seemed to be the problem in this case. Because I don’t worry much about how sessions are saved, I changed it to cake in core.php and it solved this problem, not a really good solution, but I’m fine with it.
2. Timed out Socket connections
During the import process I had to make some Socket Connections and however there suddenly was no connection possible anymore after 10-15 requests, so the following ran into timeout.
3. The solution: AjaxQueue
After some search and search and search, I finally found a script called AjaxQueue posted on a mailing list. There you can set the maximum amout of simultaneous Ajax requests, exactly what I was looking for. After some testing it turned out to do a really wonderful job and all my problems were solved without loosing much of performance.
The following code is for the Prototype framework, but it should be no problem to adapt it to other frameworks in replacing the “Ajax.”-statements to similar ones of another framework.
var AjaxQueue = {
batchSize: 1, //No.of simultaneous AJAX requests allowed, Default : 1
urlQueue: [], //Request URLs will be pushed into this array
elementsQueue: [], //Element IDs of elements to be updated on completion of a request ( as in Ajax.Updater )
optionsQueue: [], //Request options will be pushed into this array
setBatchSize: function(bSize){ //Method to set a different batch size. Recommended: Set batchSize before making requests
this.batchSize = bSize;
},
push: function(url, options, elementID){ //Push the request in the queue. elementID is optional and required only for Ajax.Updater calls
this.urlQueue.push(url);
this.optionsQueue.push(options);
if(elementID!=null){
this.elementsQueue.push(elementID);
} else {
this.elementsQueue.push("NOTSPECIFIED");
}
this._processNext();
},
_processNext: function() { // Method for processing the requests in the queue. Private method. Don't call it explicitly
if(Ajax.activeRequestCount < AjaxQueue.batchSize) // Check if the currently processing request count is less than batch size
{
if(AjaxQueue.elementsQueue.first()=="NOTSPECIFIED") { //Check if an elementID was specified
// Call Ajax.Request if no ElementID specified
//Call Ajax.Request on the first item in the queue and remove it from the queue
new Ajax.Request(AjaxQueue.urlQueue.shift(), AjaxQueue.optionsQueue.shift());
var junk = AjaxQueue.elementsQueue.shift();
} else {
// Call Ajax.Updater if an ElementID was specified.
//Call Ajax.Updater on the first item in the queue and remove it from the queue
new Ajax.Updater(AjaxQueue.elementsQueue.shift(), AjaxQueue.urlQueue.shift(), AjaxQueue.optionsQueue.shift());
}
}
}
};
Ajax.Responders.register({
//Call AjaxQueue._processNext on completion ( success / failure) of any AJAX call.
onComplete: AjaxQueue._processNext
});
/************* SYNTAX ***************
AjaxQueue.setBatchSize(size);
AjaxQueue.push(URL , OPTIONS, [ElementID]);
************** USAGE ***************
AjaxQueue.setBatchSize(4);
AjaxQueue.push("http://www.testingqueue.com/process/",{onSucess: funcSuccess, onfailure: funcFailure});
AjaxQueue.push("http://www.testingqueue.com/process1/",{onSucess: funcSuccess1, onfailure: funcFailure1}, "myDiv");
AjaxQueue.push("http://www.testingqueue.com/process2/",{onSucess: funcSuccess2, onfailure: funcFailure2});
AjaxQueue.push("http://www.testingqueue.com/process3/",{onSucess: funcSuccess3, onfailure: funcFailure3});
AjaxQueue.push("http://www.testingqueue.com/process4/",{onSucess: funcSuccess4, onfailure: funcFailure4});
AjaxQueue.push("http://www.testingqueue.com/process5/",{onSucess: funcSuccess5, onfailure: funcFailure5});
**********************************/
on March 24, 2008 on 12:29 pm
well done, bro
on July 3, 2008 on 3:15 pm
Hello compliment for your site and articles..
CAn you give me some example in ajax cake to make a mailing list…
I need that every email sent, I recieve feedback…
Is it possible?
Thanks.
on July 5, 2008 on 9:20 am
Hi,
This should be no problem, you just have to loop through your list of mails and push the ajax-action that sends the mail into the AjaxQueue. I give you my code as example, it’s basically the same, I just loop through a list of media-sources(paths) and import them. The import is indicated on the website in a progress bar:
The code is from the controller-action that is called when I press the import-button, in your case it’s the send-button.
foreach ($newMedia as $path) {
if (!empty($path)) {
$path = urlencode($path);
echo $javascript->codeBlock(“$(’source_”.$source['Source']['id'].”_progress’).innerHTML = ‘pending’;”
.”AjaxQueue.push(’sources/import”.$type.”/”.$id.”/?path=”.$path.”‘, ”
.”{onSuccess:function(){updateProgress(’source_”.$id.”‘, “.$importCounter++.”, “.count($newMedia).”);}}, ’source_”.$id.”‘)”);
}
}
Hope this helps!
on August 12, 2008 on 2:19 pm
Is this only for prototype? Please specify
on August 12, 2008 on 3:02 pm
Thanks for your comment, see the changes.
on December 30, 2008 on 6:01 pm
Is there a way to keep trace of the request’s ids and use them onSuccess?
I am sending multiple requests using this script andloading urls from a csv I’d like to place on the success function the url’s id of each single request in order to identify the url used for each response.
I am trying to use the elementID to set the id of the pushed url and retrieve it onSuccess, but it doesn’t respect the order!!
Thanks in advance.
on December 31, 2008 on 12:24 pm
If I understand you right, you want to have an id of your request url be known in the onSuccess function, so that you can identify an ending request.
I think you have to set it in the onSuccess function when writing it, so I would try something like this (in PHP):
echo "AjaxQueue.push('".$url."/".$id."', {onSuccess:function(){alert('".$id."');}}, elementID);";Hope this helps.
on April 9, 2009 on 10:16 pm
hi, its really useful
on April 15, 2009 on 2:02 pm
This is quite a hot information. I’ll share it on Delicious.
on April 21, 2009 on 7:02 am
Can you please help me with my problem ?
When an user tries to send AJAX requests simultaneously from multiple browser tabs, one request get completed and the page loads but the other AJAX calls are preempted. AS a result of which the response is empty for the other calls. Only one call survives. In my application using struts 2.0, JSP and javascript and the prototype framework, i found that the server response is empty in the cases mentioned above though the data gets updated in teh database with the request parameters. The onSucess event handler for Ajax.request gets called but the the response is empty.
Can you please help?
Thanks
on April 21, 2009 on 2:24 pm
Your problem doesn’t sound like a javascript/ajax problem, because the calls should run in different environments each browser tab, and they obviously get properly executed.
I can only guess that there is a problem within the function you are calling. Maybe the database gets read locked while writing, or anything else that prevents the view from rendering for the following calls. But I am not familiar with struts and JSP, so I doubt I can help you with this.