The Problem
Imagine for a moment that you’re building a web application based nearly 100% percent on AJAX. And imagine that you’ll likely need to send bursts of multiple AJAX requests to your server, and you also need all of these requests to be processed in order when they hit your server. You want a near continuous AJAX connection – every action, every edit, every keystroke the user makes – all of these need to be sent to your server for processing. You are building Listotron.
This is exactly the problem I’ve created for myself by working on the Listotron project with Buck. Listotron is [will be] a simple web app that lets users easily create and edit lists and collaborate on them in real time. Simple put, it’ll be a web based OmniOutliner with a much smaller feature set and multi-user realtime editing. I’m particularly excited to use it as a research playground for optimizing web-apps. (As we build, we’ll be sharing our ideas, plans, code, prototypes, and much more – so check out the Listotron blog for more).
Since we’re designing Listotron to be a 100% AJAX based application, every edit the user makes will need to be AJAX’d to the server. What’s more, we want users to be able collaboratively edit the same list at the same time – Google Spreadsheets style. This means that every edit, every tab, every indent, everything needs to be AJAX’d quickly and efficiently to our server so we can update any collaborators that might also be working on the list.
Most importantly, all of the edits need to happen in order, since most edits to the list will depend on previous edits to the list.
My goals for speed are to make the real-time editing even faster than Google’s collaborative spreadsheet editing. A large part of the perceived speed of collaboration is the frequency of AJAX calls (the more calls per second the faster the app can update collaborators) – so I’ll need both an incredibly streamlined server-side and an incredibly optimized AJAX front-end in JavaScript. This article will talk about the JavaScript strategy.
Asynchronous AJAX
The problem with relying on plain ole’ asynchronous AJAX is that it’s, well, asynchronous. We’re not guaranteed that all of our AJAX calls will be received by the sever in the correct order, and we’re certainly not guaranteed that we’ll receive the responses in the correct order.
Imagine sending off 4+ edits to the sever, only to find out in the last response that the 2nd request failed. What if edits 3 and 4 relied on edit 2? How do I roll back the UI? How do I maintain data integrity on the server? on the client? It’d be hell to build, hell to debug, and hell to maintain. No thanks!
Synchronous AJAX
Since I need to send all my AJAX requests to the server in order, why not just use synchronous ajax calls? Problem solved, right? Unfortunately, it’s not that simple – from the YUI blog:
XMLHttpRequest can operate synchronously or asynchronously. Many people prefer to use it synchronously. When used this way, the JavaScript engine is blocked until the interaction with the server is complete… Temporal complexity is abstracted away, leaving a very familiar and comfortable programming pattern.
So far so good, but here’s the zinger (emphasis added)…
Because the JavaScript engine is blocked until the request completes, the browser will be frozen. The user cannot cancel the request, cannot click away, cannot go to another tab. This is extremely bad behavior.
That’s very bad news indeed. Synchronous AJAX ends up looking something like this:
Clearly this is not the way to go. It’s simply not acceptable to freeze the entire browser – (even in Chrome‘s case: the entire tab) – for every single tiny edit. A different approach needs to be made.
The Solution
The solution we’ll be using with Listotron is to manually enforce synchronization into what would otherwise be asynchronous AJAX calls. The idea is this: after an AJAX request is made, queue up any other requests that need to be made until a response comes back – then send a single bundled AJAX request that contains all of the requests currently in the queue. The server decodes this bundled AJAX call, processes each request, and sends back a bundled response. All this ends up looking something like:
Since we need to maintain the call-order of our AJAX calls, we can queue them up in-order and then send off multiple requests to our server in one go. The benefits are: (1) fewer http connections hitting the server, and (2) the order of the AJAX calls is maintained, (3) and UI is still very responsive. And, since the server sees multiple edits at the same time, further optimization can be done that would otherwise be impossible. For instance, the server receives 2 commands in a bundle: an indent command followed by an outdent command. The net result to the list is to do nothing!
This optimization comes at a price, of course, and it’s that the server needs some custom code to handle these bundled AJAX requests, so this solution isn’t as easy as dropping in a new JavaScript framework or function.
Bringing It To Life
I’ve set up a basic demo and tutorial that bundles up asynchronous AJAX calls to maintain their order. I’ve implemented everything as a jQuery plugin with a lightweight PHP/MySQL server-side. Check it out here.
(This post is a cross-posted on the Listotron blog)
It seems like in Firefox 3 Synchronous AJAX doesn’t freeze UI.
Did you consider using JSON-RPC? It supports batch requests:
http://groups.google.com/group/json-rpc/web/json-rpc-2-0