Leveraging Deferreds in Backbonejs

TL;DR – Assigning Deferreds as Model / Collection properties can make your life much easier.

UPDATE: We're holding a full day Intro to Backbone.js workshop on June 16th, 2012. Details here.

We've been working with Backbone.js a lot this year and this is one of the best tricks we've discovered for making complex Backbone apps a bit easier to manage. Most Backbone apps require some sort of bootstrapping process to initialize themselves with data. In a perfect world, you can populate your application with JSON by direct reference to your backend code, as illustrated by this example from the Backbone Docs:

But for the majority of implementations, this just isn't practical to do. Either you have too much data to load it synchronously or you may not even have a conventional view layer connected to your backend. In any case, we've found a clever solution to getting data and rendering in a non-blocking way.

Deferreds, wut?!?

Deferred Objects have been part of jQuery since the 1.5 release and are an underlying part of all AJAX functions in jQuery. If you're looking for a more detailed explaination of how Deferreds work, check out Nico's post on the subject. For now, let's settle for the pithy explaination from the jQuery API Docs:

"[Deferred objects are] chainable utility object[s] that can register multiple callbacks into callback queues, invoke callback queues, and relay the success or failure state of any synchronous or asynchronous function."

Basically, if you have access to a deferred object, you can reliably bind callback functions to the state of a function now and forever, regardless of how or when that function is "resolved."

Now what's really cool is that jQuery AJAX functions return a Deferred object. This is what lets you chain callbacks directly off of AJAX methods, but it's even better when you can leverage that returned value from a completely different scope. This becomes a really important behavior in a Backbone app, but only if you know how to take advantage of it.

Using Deferreds with Backbone

First, let's take a look at a simple Backbone example:

Press the "run" button on the upper right corner to see it's output.

Put them in your Collections & Models

Let's start off with a Backbone Collection (or Model) that we want to automatically populate itself with data on creation. All we need to do is make a call to this.fetch() in the initializer, right? Absolutely. But the difference is that we assign the Deferred object returned from this.fetch to Collection.deferred.

Access them in your Views

Now there's no reason why you'd need to do this in the initialize method, but it makes grabbing a Collection's data as simple as creating an instance of it. This probably doesn't look much different than what you're already doing with Backbone. But here's the fun part:

There are a few things at play in this example: we're defining a basic view, and instantiating it along with the collection from above. When we create the view instance, our collection is passed into it, linking the two together in our app. This allows us to reference the collection from within our view.

Solve state problems by ignoring them

Notice how we're able to call myView.render() as soon as we've created the view instance? This is because there's a deferred object between us and rendering the view–and we can rely on that to execute this as soon as it's ready. When render creates a callback off of the collection's deferred object, we know that this method won't be called until the collection.fetch() is complete.

There's no need for us to check anything other than the deferred object to know whether the request has succeeded or failed.

By assigning the deferreds issued by Model and Collection methods which return AJAX calls, it becomes really easy to code Backbone Apps that load data asynchronously, without having to write any extra code to check if your data is loaded.

And $.when() you've got more than 1…

For extra credit, you can use jQuery.when() to handle multiple deferred objects simultaneously and when you need to deal with objects that might be deferreds. Here's a quick example:

When I'm not replying to Backbone issues on Github, you can usually find me in #documentcloud on Freenode IRC, nick wookiehangover. Please hit me up with any questions!

Update [11/25/11]: While playing around with different ways of using Common JS Promises outside of jQuery, I implemented jQuery style deferreds as an Underscore module. The project is available on Github if you're interested!

Update [12/4/11]: Even better, updated interactive example with jsfiddle, added info about $.when