Profile picture of Liam Moat

Liam Moat

Principal Software Engineer at Microsoft

Backbone.js and Bluebird

By Liam Moat. . 2 minutes read.

In this blog post I will explain how Backbone.js models can be extended to return an A+ Promise, rather than jQuery’s jqXHR object.

Backbone.js is a JavaScript framework providing models, collections and views to your client-side application.

Bluebird is a full featured Promise/A+ implementation with exceptionally good performance for JavaScript.

Background

In Backbone, a Model is used to:

Backbone uses a function called Backbone.sync every time it attempts to read or save a model to the server. For example, this function is used every time you invoke .save() and .destroy() on a model. By default, it uses Backbone.ajax to make a RESTful JSON request and returns a jqXHR object from jQuery.

As of jQuery 1.5 the jqXHR object implements a Promise interface, giving them all the properties, methods, and behaviours of a Promise (see Deferred object).

A+ Promise instead of jqXHR

When working with Promises, I prefer to use Bluebird - which implements the A+ Promise Standard. For example, rather than using jQuery’s .done, .fail and .always I prefer to use .then, .catch and .finally from Bluebird. This helps ensure all my asynchronous code is inline with the A+ specification.

import { Model } from 'backbone';

let User = Model.extend({ urlRoot : '/users' });
let user = new Book({ id: 1234 });

user.save()
.then(function (res) { console.log('success'); } )
.catch(function (err) { console.log('error', err); });

With Backbone you can override the default behaviour for both Backbone.sync and Backbone.ajax.

Because we still want to use AJAX, in this example, we will override Backbone.ajax. This is the default implementation:

Backbone.ajax = function() {
  return Backbone.$.ajax.apply(Backbone.$, arguments);
};

We can use Bluebird’s Promise.resolve function to convert jQuery’s jqXHR object to an A+ Promise.

import Backbone from 'backbone';
import Promise from 'bluebird';

Backbone.ajax = function() {
  let xhr = Backbone.$.ajax.apply(Backbone.$, arguments);
  return Promise.resolve(xhr);
};

Collections too

This also works for .fetch on a Backbone Collection.

import { Collection } from 'backbone';

let Notes = Collection.extend({ url: '/notes' });
let notes = new Notes();

notes.fetch()
.then(function (res) { console.log('success'); } )
.catch(function (err) { console.log('error', err); });