Fresh Blurbs

Async Data Loading in Knockout.js Without the 'Callback 'Hell'

Knockout.js is a beautifully implemented MVVM Javascript framework that makes creating highly dynamic, modern, Ajax-driven user-interfaces a breeze and almost completely removes the need to directly mess with HTML DOM, thus drastically decreasing the complexity of front-end code and making things typically complicated -- trivial.

Full introduction to Knockout.js is both way beyond this blog post, as well as: unnecessary. Knockout is possibly the best documented open-source framework I have ever seen. It even comes with wonderful, interactive tutorials that can get you started in no time.

The use-case I want to cover in this post is: async loading of multiple parts of the page, without causing the proverbial "javascript callback hell". The bonus part will cover implementing a handy "loading" indicator. While I will cover the technique in the context of Knockout.js, the same principal applies to any MVVM framework (or even if you are just fiddling around with jQuery directly). That said, understanding of Knockout is required to get the most of the description, so head over to the documentation, if you are unfamiliar with Knockout. It won't take too long to get a grasp of. Promise.

Setup

Let's assume we are using Knockout to implement a somewhat typical "Edit User" page. The model behind the page is a user object, which we will load/save via Ajax. However, a user must be able to also pick: country of residence and his/her language preference. Here's a catch: we want the list of country options and the list of available languages to also load via Ajax (say, using some API call). Furthermore, we want countries and languages to load in parallel, since the two do not depend on each other and doing things in parallel speeds overall page load time, obviously.

The somewhat complicated part is that: we do need to make sure that both the country list, as well as languages list are loaded _before_ we load the user object. This is necessary so that Knockout gets a chance to populate options on corresponding drop-downs before Knockout tries to set current user value on those drop-downs. Last, but not least, we would like to show a user a "loading" visual indicator, while things are loading over Ajax calls, just in case any of them take some time.

HTML part

Following are some key parts of the HTML markup we will need for this exercise. For the sake of brevity only certain snippets are shown:

Github gist link: https://gist.github.com/2642420

Please note that we are using Knockout Mapping plugin. Related to it, please note in the HTML (and below in Javascript) that data properties are encapsulated on a "user" property of the Knockout model object, not: directly on the model. I personally find that this makes using the mapping plugin, while being able to have other logical methods directly on the model - much easier/safer/better.

Javascript part

Much like with the HTML, only the most important parts of the Javascript/Knockout side are shown, for the sake of brevity:

Github gist link: https://gist.github.com/2642523

What Did Just Happen?

Probably the most important part of the code is the following snippet:

var parallelExecutions = [self.loadCountries, self.loadLanguages];

var delayedLoadUser = _.after(parallelExecutions.length, self.loadUser);

_.each(parallelExecutions, function(func) {
    func({success: delayedLoadUser});
});        

It uses the after() method from the Underscore.js library to run loadContries() and loadLanguages() functions asynchronously and in parallel, but waits until both of them complete before running loadUser() function. The delay is achieved by creating the delayedLoasUser() wrapper method.

Note: if you have written server-side, Node.js Javascript you are probably familiar with the wonderful Async.js and wondering why am I using Underscore where Async's parallel() method might work great. It would and if I was writing server-side code, I probably would have used Async over Underscore, in this case, but minified Async.js is 108KB vs Underscore's 13K, so the latter felt a more prudent choice for client-side code.

Please also note that the loading indicator is an array, rather than a scalar variable. This allows us to treat it as a stack, pushing a value when something starts loading and popping a value when something finishes loading, supporting parallel loading of many request in a clean and simple way.

comments powered by Disqus