Using jQuery Deferred to Wait for Multiple Backbone Models to Save

Backbone's Model implementation is great for most things, but one thing I've had a hard time with is waiting for multiple models to save before proceeding. Backbone offers a success callback like this:

model.save
  success: ->
    alert("We did it!")

You could also use the sync callback like this:

model.on 'sync', ->
  alert("We did it!")
model.save()

But what about when you want to wait for multiple models to finish saving, all with their own asynchronous requests?

Don't Nest It. Chain It!

The jQuery Deferred object is a chainable utility object that can register multiple callbacks to relay the success or failure state of an asynchronous operation. Lucky for us, Backbone's model.save() method returns a jqXHR object, which implements the Deferred API. This means that instead of writing this:

model.save
  success: ->
    alert("We did it!")

We can write this:

model.save().done(-> alert("We did it!"))

That's a nice bit of syntactic sugar, but it still doesn't address our original problem: How can we wait for multiple models to save, and then fire the callback to alert the user?

Tell Me When You're All Done

jQuery.when allows us to combine multiple Deferred objects into one aggregate Deferred object, such that we can chain callbacks to be executed only when all the objects have resolved.

For sake of example, let's say we have a collection of 3 Backbone models we'd like to save:

collection = new MyCollection([{name: "Steve"}, {name: "Dave"}, {name: "Tom"}])

Remember that Backbone's model.save() returns a jqXHR object, which acts as a Deferred. So we can run:

xhrs = collection.map (model) -> model.save()

This will create an array xhrs containing the jqXHR objects for each individual save operation. To alert the user when all of them complete, we can use jQuery.when:

jQuery.when(xhrs...).done(-> alert("All of them are saved!"))

Note: The splat (...) syntax above is required to split the xhrs array into separate arguments. This had me stumped---without the splat, jQuery treats the array as a single Deferred object, which obviously doesn't execute the callbacks in the same manner as multiple jqXHR objects.

And Tell Me When One of You Failed

We can also use Deferred's fail() method to alert the user that one or more of the save operations failed:

jQuery.when(xhrs...).
  done(-> alert("We succeeded!")).
  fail(-> alert("We failed."))

Conclusion

The jQuery Deferred API is a powerful way to elegantly wait for the completion of asynchronous operations in your Backbone application. While it's tempting to resort to workarounds like using setTimeout to wait an arbitrary amount of time for operations to complete, using jQuery.when means you don't introduce race conditions into your application.

If you have any questions or if something isn't working as described above, please leave me a comment. I'll try my best to answer as soon as I can.

Building a new Digital Product? Read This First

You're about to build your first digital product, but you're terrified at the breadth of terminology and wary of consultants nickel-and-diming you.

My free book Why Software Projects Fail offers that framework. In this companion to your hiring and discovery process, you'll learn how to inform your next decisions and to empower yourself along the way.

In the book, you'll learn:

  • How to find and hire a trustworthy consultant
  • Why it's critical you pay for a software discovery
  • How to assess your consultant's bid
  • What to expect—and be wary of—during the development process
  • How to take control of your project

Enter your email address below and then click the "Send Me My Free Gift" button. I'll send you Why Software Projects Fail, and you'll be equipped for success on your next project.