Asynchronous UIs - the future of web user interfaces

It’s an interesting time to be working on the frontend now. We have new technologies such as HTML5, CSS3, Canvas and WebGL; all of which greatly increase the possibilities for web application development. The world is our oyster!

However, there’s also another trend I’ve noticed. Web developers are still stuck in the request/response mindset. I call it the ‘click and wait’ approach - where every UI interaction results in a delay before another interaction can be performed. That’s the process they’ve used their entire careers so it’s no wonder most developers are blinkered to the alternatives.

Speed matters; a lot. Or to be precise, perceived speed matters a lot. Speed is a critical and often neglected part of UI design, but it can make a huge difference to user experience, engagement and revenue.

Yet, despite all this evidence, developers still insist on using the request/response model. Even the introduction of Ajax hasn’t improved the scene much, replacing blank loading states with spinners. There’s no technical reason why we’re still in this state of affairs, it’s purely conceptual.

A good example of the problem is Gmail’s ‘sending’ notification; how is this useful to people? What’s the point of blocking? 99% of the time the email will be sent just fine.

Why oh why?

As developers, we should optimize for the most likely scenario. Behavior like this reminds me of Window’s balloon notifications, which were awesome for telling you about something you just did:

No shit sherlock

The solution

I’ve been working on this problem, specifically with a MVC JavaScript framework called Spine, and implementing what I’ve dubbed asynchronous user interfaces, or AUIs. The key to this is that interfaces should be completely non-blocking. Interactions should be resolved instantly; there should be no loading messages or spinners. Requests to the server should be decoupled from the interface.

The key thing to remember is that users don’t care about Ajax. They don’t give a damn if a request to the server is still pending. They don’t want loading messages. Users would just like to use your application without any interruptions.

The result

AUIs result in a significantly better user experience, more akin to what people are used to on the desktop than the web. Here’s an example of an AUI Spine application with a Rails backend.

AUI

Notice that any action you take, such as updating a page, is completely asynchronous and instant. Ajax REST calls are sent off to Rails in the background after the UI has updated. It’s a much better user experience.

Compare it to the static version of the same application, which blocks and navigates to a new page on every interaction. The AUI experience is a big improvement, that will get even more noticeable in larger (and slower) applications.

Have a browse round the source to see what’s going on, especially the main controller.

Not a silver bullet

It’s worth mentioning here that I don’t think this approach is a silver bullet for all web applications, and it won’t be appropriate for all use cases. One example that springs to mind is credit-card transactions, something you’ll always want to be synchronous and blocking. However, I do believe that AUIs are applicable the vast majority of the time.

The other point I’d like to make is that not all feedback is bad. Unobtrusive feedback that’s actually useful to your users is completely fine; like a spinner indicating when files have synced, or network connections have finished. The key thing is that feedback is useful and doesn’t block further interaction.

The implementation

So how do you achieve these AUIs? There are a number of key principles:

Now, these concepts turn the existing server-driven model on its head. It’s often not possible to convert a conventional web application into a client side app; you need to set out from the get go with these concepts in mind as they involve a significantly different architecture.

Moving state to the client side is a huge subject and beyond the scope of this article. For more information on that, you might want to read my book: JavaScript Web Applications. Instead in this post I want to focus in on a specific part of AUIs; asynchronous server communication, or in other words: server interaction that’s decoupled from the user interface.

The idea is that you update the client before you send an Ajax request to the server. For example, say a user updated a page name in a CMS. With an asynchronous UI, the name change would be immediately reflected in the application, without any loading or pending messages. The UI is available for further interaction instantly. The Ajax request specifying the name change would then be sent off separately in the background. At no point does the application depend on the Ajax request for further interaction.

For example, let’s take a Spine Model called Page. Say we update it in a controller, changing its name:

page = Page.find(1)
page.name = "Hello World"
page.save()

As soon as you call save(), Spine will perform the following actions:

  1. Run validation callbacks and persist the changes to memory
  2. Fire the change event and update the user interface
  3. Send an Ajax PUT to the server indicating the change

Notice that the Ajax request to the server has been sent after the UI has been updated, in other words what we’ve got here is an asynchronous interface.

Synchronizing state

Now this is all very well in principle, but I’m sure you’re already thinking of scenarios where this breaks down. Since I’ve been working with these types of applications for a while, I can hopefully address some of these concerns. Feel free to skip the next few sections if you’re not interested in the finer details.

Validation

What if server validation fails? The client thinks the action has already succeeded, so they’ll be pretty surprised if subsequently told that the validation had failed.

There’s a pretty simple solution to this: client-side validation. Replicate server-side validation on the client side, performing the same checks. You’ll always ultimately need server-side validation, since you can’t trust clients. But, by also validating on the client you can be confident a request will be successful. Server-side validation should only fail if there’s a flaw in your client-side validation.

That said, not all validation is possible on the client-side, especially validating the uniqueness of an attribute (an operation which requires DB access). There’s no easy solution to this, but there is a discussion covering various options in Spine’s documentation.

Network failures & server errors

What happens if the user closes their browser before a request has completed? This is fairly simple to resolve, just listen to the window.onbeforeunload event, check to see if Ajax requests are still pending, and if appropriate notify the user. Spine’s Ajax documentation contains a discussion about this.

window.onbeforeunload = ->
  if Spine.Ajax.pending
    '''Data is still being sent to the server; 
       you may lose unsaved changes if you close the page.'''

Alternatively, if the server returns an unsuccessful response code, say a 500, or the network request fails, we can catch that in a global error handler and notify the user. Again, this is an exceptional event, so it’s not worth investing too much developer time into. We can just log the event, inform the user and perhaps refresh the page to re-sync state.

ID generation

IDs are useful to refer to client-side records, and are used extensively throughout JavaScript frameworks like Backbone and Spine. However, this throws up a bit of a dilemma - where are the IDs generated, the server or the client?

Generating IDs on the server has the advantage that IDs are guaranteed to be unique, but generating them on the client side has the advantage that they’re available instantly. How do we resolve this situation?

Well, a solution that Backbone uses is generating an internal cid (or client id). You can use this cid temporarily before the server responds with the real identifier. Backbone has a separate record retrieval API, depending on whether you’re using a cid, or a real id.

Users.getByCid(internalID)
Users.get(serverID)

I’m not such a fan of that solution, so I’ve taken a different tack with Spine. Spine generates pseudo GUIDs internally when creating records (unless you specify an ID yourself). It’ll use that ID to identify records from then on. However, if the response from an Ajax create request to the server returns a new ID, Spine will switch to using the server specified ID. Both the new and old ID will still work, and the API to find records is still the same.

Synchronous requests

The last issue is with Ajax requests that get sent out in parallel. If a user creates a record, and then immediately updates the same record, two Ajax requests will be sent out at the same time, a POST and a PUT. However, if the server processes the ‘update’ request before the ‘create’ one, it’ll freak out. It has no idea what record needs updating, as the record hasn’t been created yet.

The solution to this is to pipeline Ajax requests, transmitting them serially. Spine does this by default, queuing up POST, PUT and DELETE Ajax requests so they’re sent one at a time. The next request sent only after the previous one has returned successfully.

Next steps

So that’s a pretty thorough introduction into asynchronous interfaces, and even if you glossed over the finer details, I hope you’ve been left with the impression that AUIs are a huge improvement over the status quo, and a valid option when building web applications.

If you’re interested in building AUIs, you should definitely check out Spine, and Spine’s Ajax guide.


This is a repost of an article first posted in November 2011

 
636
Kudos
 
636
Kudos

Read this next

Killing a library

A programming library’s main mission should be to make itself redundant, and with that in mind that I’m announcing the deprecation of Juggernaut. Juggernaut was one of the first solutions to pushing data in realtime to... Continue →