Indeed, there's a lot to like about a technique that allows you to update your webpage with a snippet of information retrieved from the server *without* having to reload the whole sorry spectacle. It allows for much greater range of interaction with the user, as well. As I type this blog entry, a small message at the foot of the page is reassuring me that my draft is being stored automatically. How? Presumably a little java applet is being fired at regular intervals, which invokes an XMLhttpRequest (or XMLObject, if you're using IE 6 or earlier), and sends the current data back to the server.
Well, having eulogized the technique, what have I found to grouch about?
As an old song put it: 'nice legs, shame about the face!'. One thing, in particular, has been annoying me about the callback mechanism. On just about every tutorial on the subject (and since there are plenty, rather than reinvent the wheel here, I suggest you go google 'Ajax tutorial'! ) you will see something like the following code:
Perfectly workable as is, and quite adequate for a simple starting tutorial on how to make an AJAX call.
Except, how does the callback function know where to retrieve the results? It doesn't get provided as a call parameter. Nope! The callback function has to go grubbing around for a global variable! This is ugly, and I don't just mean in the cosmetic sense either.
It Gets Worse
While Quirksmode has an interesting account of what can happen, and how to avoid the worst of it, there is an astonishing paucity of advice on this topic. So, I had a bit of a tinker, and here is my solution.
Preliminary Tidying Up
First of all, I like a place for everything, and everything in its place. Just as a server process might create a short lived thread/process to handle each request it receives, I would like to see each Ajax call result handled by a single object.
If XMlhttpRequest passed itself to the call back function, we would have a solution (and I wouldn't have a topic). Can we simulate this?
Of course we can, by re-writing the above listing as a wrapper function that implements a chain of command. The main routine then becomes:
This separates the code handling AJAX, which now reads as follows.
An Obvious Solution...
Well, this might look a bit tidier but, apart from that, nothing much has been achieved: while the callback function proper now has a handler provided to it, it's still going to be the same handler each time a call is made. We want to create a request object each time we make a call.
So, would this do?
...That Doesn't Work!
However, there is a way around this: retain the global handler as an array, and refer to the required handler by its array index:
Now we're beginning to cook! Each new request handler is stored in a static array, and the dummy callback function refers to it by its index. This is important because to refer to a handler specifically will cause the same problem as before: several callback functions referring to an indeterminate handler.
Why this is so has something to do with the way the dummy function is generated (it is a class) . To refer to the handler object itself is to refer to it by reference (ie the address of the temporary script variable) to refer to the array index is to refer to it by value (an integer). The latter approach ensures that each callback method refers to a specific handler.
I haven't been using it long enough to give a definitive judgement but, so far, this technique seems to work well. I do have some qualms about that array of request objects growing in the corner like a stack of dirty dishes. It should get cleared away each time the page is refreshed. However, it may cause memory problems if the page persists for long. I am loath to suggest removing old objects from the array since it would upset the index references of current Ajax calls. If this is a concern, you could replace each object entry with an integer as it completes.
One final embellishment to the procedure. Since we now have a single definition of a callback function, we can use it to perform useful universal tasks such as telling whether or not the server side script ran properly. eg, when returning the result as XML, this is readily done by checking to see whether or not the responseXML field was generated. If it wasn't, then the raw text may contain the warning diagnostics. Similar results can be achieved by searching the result Text field for error indicators.
OK? Now go and play.
The following is the complete final listing: