I recently created a visualisation that showed the difference between rich and poor schools in New Zealand. This visualisation used a series of relatively complex animations to introduce and convey information to the viewer.

In this post I will describe how I used Q and JQuery promises to compose the complex animations and give examples of how using promises can be beneficial for such a visualisation.

Note: I am going to use CoffeeScript in this post for reasons described here. To learn CoffeeScript perhaps you could try The Little Book on CoffeeScript.

The Visualisation

http://bl.ocks.org/grahamjenson/raw/9168767/

View the full code here

For this visualisation I wanted to try the style where statistics are presented as a set of icons that each represent many people, similar in style to this video. I find this type of visualisation quite powerful as it provides an easy way for people to understand large scale issues by compressing many data points into single icon.

Q and JQuery

For this project I used the Q promises to direct the overall flow of the visualisation, and JQuery promises (which I have previously discussed here) to create the animation effects. One of the great things about Q is that it is able to use JQuery promises, so I had no problems with their integration.

I decided to use Q, instead of just JQuery, because I wanted to start using promises that conform to the Promises/A+ specification. Q also added some useful functions, like delay, which waits a set time to resolve a promise.

The Basic Promises

The way in which I chose to animate using promises is to create very simple functions that return a promise, then compose them together either to either be completed in parallel with Q.all or in sequence with then.

This is the show_message function I wrote to display text in the visualisation: show_message = (message, delay = 2000) -> Q.fcall( -> $('.messages').html(message) ) .then(-> $('.messages').show('scale',100)).delay(delay) .then(-> $('.messages').hide('scale',100)) .then(-> $('.messages').html(''))

This function takes a message string and a delay to show the message for delay time. First show_message uses Q.fcall to set the message as the html of the messages class. Then it shows the message using the scale effect, waits for the delayed time, then hides the message. Finally, it removes the message from the html.

The Steps

Removing the complexities of the smaller animation functions, then I could begin to think of the steps the visualisation would need. Each step is a unit of animation described with a promise, for example: step3 = -> p = show_message("Let's distribute them into NCEA Level 3") return p.then( -> all_subjects.get_deciles().sort_out_deciles() )

This step3 function returns a promise to display a message with show_message then execute the sort_out_deciles animation. sort_out_deciles returns a promise for its completion, therefore the step3 promise will not be resolved until the sort_out_deciles animation is complete.

If I were to change this step, by adding more animations or changing the length of time the animations run for, the returned promise will not be resolved till the entire step is completed. This means that all following steps will also wait for this step to complete, meaning no further changes are needed.

Putting it all together

By linking the steps together using promises and delay to alter the timing of the animations, you can finely tweak the resulting visualisation. $('.play').on('click', -> $('.play').hide() step1().delay(500) .then(step2).delay(500) .then(step3).delay(500) .then(step4).delay(500) .then(step5).delay(500) .fail(console.log)

I decided to include a play button in this visualisation to enable the user to select when to start the visualisation. Once this button is clicked, then a series of steps are promised to be executed. In this visualisation the final step, step5, alters the visualisation to enable further interaction with the visualisation.

Note: Q will swallow your errors unless you log, or otherwise handle, the errors using the fail function. This is necessary to debug any problems.

Conclusion

Using Q and JQuery promises is a really elegant way to compose a visualisation and it let me cleanly build complex animations out of simple promises. It also had the benefit of being able to change the complex functions and have all the steps adapt without any additional work. I enjoyed writing this project, in part because of the use of promises, and I would like to further explore this idea in the future.

Related reading

  1. I promise this will be short a post about JQuery promises
  2. Learning jQuery Deferreds: Taming Callback Hell with Deferreds and Promises
  3. JavaScript with Promises