Tuesday, June 05, 2007 Vienna 1 Comments
When building Wimbomedia, I encountered the problem of managing multiple HTTPRequests. In order that a new HTTPRequest does not interrupt or kill the previous one, I needed some sort of queuing abstraction that will wait until the previous request was completed (or aborted) before moving on to the next one. FIFO is a computer acronym for a concept called "First In First Out". It is a common principle in managing messages on operating systems because, lest we forget, even offline platforms have to deal with lag!
Imagine a hungry customer arrives at a counter in McDonalds to find a family of five in the throes of indecision. The person serving at the counter would wait until the family has received their order or decided they fancy Berger King instead, before asking the hungery guy to place his order. Well that's FIFO right ther — "First come, first served"! And that's how I'll propose to solve the multiple Ajax request problem.
We start by creating a namespace in which to put our functions, objects, methods, and variables. If you are already scratching your head at this point, then read this post on what namespaces are.
Let's call the namespace for this Ajax engine "Ajax". To do this we'll create an object "Ajax". In this object we will add a number of variables:
- state – a property we will use to determine what to do with an ajax request when the engine receives it.
- item – another object within the Ajax object. This object contains the information about our ajax request. I will go into more detail about it later.
- queue – an array to store all the item objects.
- loadingMsg – the default message to display as a place holder while we wait for the engine to fetch our Ajax output for this request.
var Ajax = {
state : "idle",
item : {url:null, canvas:null},
queue : new Array(),
loadingMsg : "Loading..."
};
Next we have the function "call" which receives the Ajax request, and decides what to do with it. Again, notice the function is withint our namespace "Ajax". The function takes 3 parameters:
- script – a string of the url you which to request. eg. "submitvote.php?user=Tom&vote=4"
- canvas – the id of the element in which to print the response from the HTTPRequest
Ajax.call = function(script, canvas)
{
// try to show loading message
try
{
document.getElementById(canvas).innerHTML = Ajax.loadingMsg;
}
catch(err) { /* just catch the error and nothing else */ }
// Check if the engine is busy with an item
if(Ajax.state == "busy")
{
// Busy so add this request to queue and wait/return...
Ajax.queue.push({url:url, canvas:canvas});
return;
}
else
{
// We're "idle" so lets process this request
// assign the current Ajax.item to be this request
Ajax.item = {url:url, canvas:canvas};
}
// Create the HttpRequest object
if(window.XMLHttpRequest)
{
Ajax.xmlHttp = new XMLHttpRequest();
}
else if(window.ActiveXObject)
{
Ajax.xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
if(Ajax.xmlHttp != null)
{
// Append timestamp on url string to make it unique
// otherwise it might fetch the script from the cache.
var date = new Date();
var stamp = date.getTime();
var callURL = (Ajax.item.url. indexOf("?") != -1) ? Ajax.item.url + "&rand=" + stamp : Ajax.item.url + "?rand=" + stamp;
var xmlHttp = Ajax.xmlHttp;
xmlHttp.onreadystatechange = Ajax.callback;
xmlHttp.open("GET", callURL, true);
xmlHttp.send(null);
// Set the engine state to "busy" i.e. "DO NOT DISTURB"
Ajax.state = "busy";
}
}
The callback function does the job of processing and printing the HTTPRequest once it's sent. once the callback has successfully printed the HTTPRequest response text or has failed and aborted, it calls Ajax.serve to bring up the next Ajax call.
Ajax.callback = function()
{
var xmlHttp = Ajax.xmlHttp;
if(xmlHttp.readyState == 4)
{
// we need to use try / catch because of Firefox
// sometimes dies here.
try
{
// try to to get a response from the HTTPRrequest
if(xmlHttp.status == 200)
{
Ajax.response = xmlHttp.responseText;
}
else
{
Ajax.response = "Error";
}
}
catch(e)
{
// if Firefox experiences a fatal error
// catch it and move one
Ajax.serve();
}
var canvas = document.getElementById(Ajax.item.canvas);
// Try to print the response text.
// Again, if an error occurs while trying to print
// the resonse, we abort to the next item on the queue
try
{
canvas.innerHTML = Ajax.response;
}
catch(e)
{
Ajax.serve();
}
// All's well so let's serve up the next customer
Ajax.serve();
}
}
Ajax.serve = function()
{
// reset state
Ajax.state = "idle";
if(Ajax.queue .length > 0)
{
// call up the next customer on the queue
var A = Ajax.queue[0];
Ajax.call(A.url, A.canvas);
// take this customer off the waiting list
Ajax.queue.splice(0,1);
}
}
And there we have it. So a call to the engine will look something like Ajax.call("showComments.php?post=316", "comment_div"). Off course you can add more features like prioritizing certain types of requests or having more than one type of callback .
If you've found this useful let me know. I'd also be happy to clarify or re-explain anything.





Petro Jun 14 08
Standby for an updated version of this approach. I'll post a tutorial based on the work I've been doing on the Wimbomedia Sandbox