Open Source, Tech Resources

Backbone.js: A Better ‘fetch’ Function

 

how to create a better version of backbone.js fetch function. Simply let your dog be your helper.

I find the Backbone fetch function to be somewhat tricky.  This function, defined on both Backbone.Model and Backbone.Collection returns a jQuery XHR object, which also implements the Promise interface.  The idea is that you can register done, fail, and always callbacks on the returned XHR object.  In that sense, the Backbone code is well thought and enables the developer a granular level of control, since it doesn’t completely abstract away the jQuery implementation of the ajax requests.  If for example you want to abort the fetching process, all you have to do is call the jQuery AJAX abort method:

var Game = Backbone.Model.extend({...});
var GameCollection = Backbone.Collection.extend({model: Game});
var games = new GameCollection();
var request = games.fetch();

// Now if we want to abort the request, we can:
request.abort();

The problem with fetch

What I don’t like about Backbone’s fetch function is that the done promise callback you register is called with the same arguments as regular jQuery ajax callbacks:

var request = games.fetch().done(function(data, textStatus, jqXHR) {
// Note the arguments - they are jQuery specific
// there is no reference to the `games` collection
// inside this callback
});

One could argue that I could just reference games from within the callback, since it’s in the closure scope, but I beg to differ. If you want to register multiple callbacks, some of which don’t have the games collection in their closure, you’re plain out of luck.

var SomeModule = require('someModule');

// Create a closure
(function() {
var games = new GameCollection();
var request = games.fetch().done(SomeModule.doSomeMore);
// SomeModule.doSomeMore can't access the `games` collection
})();

A better version of fetch

Truth is, if the fetch succeeds, I don’t really care about the raw response data, nor the text status and the XHR object.  What I really want is for the callback to receive the backbone model \ collection after it’s been fetched and updated.  To achieve that we can define a helper function which also returns a promise, but a more meaningful one that promises the Backbone model \ collection

var fetchGames = function(options) {
var deferred = $.Deferred();
var games = new GameCollection();
var xhr = games.fetch(options).done(function(data, textStatus, jqXHR) {

// Resolve with the games collection, along with
// the jQuery arguments appended
deferred.resolve(games, data, textStatus, jqXHR);
}).fail(deferred.reject);

// "Hijack" the abort method from the xhr object to our promise object.
// This will enable us to abort the request if necessary.
// Note: `abort` must be bound to the XHR object for it to work
var promise = deferred.promise();
promise.abort = _.bind(xhr.abort, xhr);
return promise;
}

What  do we gain from this approach?

1. Now, whenever we register a done callback function, we get the Backbone model \ collection injected into it. We also pass in the regular jQuery callback arguments (data, textStatus, jqXHR) after it, to allow the developer to still access the jQuery layer if needed.

2. For the “bad path” of request failure, we just pass the arguments from the internal xhr to our wrapper promise. This is to allow examining why the request failed.

3. Noteworthy is the assignment of the xhr‘s abort function onto the promise. You might wonder why I need this. Well, if the view (or any other parent object) that calls fetchGames gets closed, it’s good practice to abort AJAX requests initiated by this view, especially if the request’s outcome pertains only to the view.  In our example, if we have a workspace view that calls fetchGames, if that view gets closed, then there’s no point in having an AJAX request hung in the air.  Once the workspace view has been closed and removed from the DOM, even if the request succeeds, there will be nowhere to append the HTML that would be rendered from the fetched games collection.

If anyone else has interesting methods to enhance Backbone, please feel free to comment or to tweet me at @gurdotan

Feel free to share:
Share this Story
Load More Related Articles
Load More By Gur Dotan
Load More In Open Source

5 Comments


  1. Marhamah

    June 4, 2014 at 8:02 am

    can we use same technique for Model.fetch()?

    Reply

    • Gur Dotan

      June 14, 2014 at 7:07 am

      Yes you can, with very minor changes

      Reply

  2. Marhamah

    June 13, 2014 at 6:49 am

    I use this for an array of model, here is my code

    fetchData: function (Models) {

    var deferred = $.Deferred();
    var xhr = $.when.apply(null, Models).done(function(data, textStatus, jqXHR) {
    deferred.resolve.apply(Models, data, textStatus, jqXHR);
    }).fail(deferred.reject);
    var promise = deferred.promise();
    //promise.abort = _.bind(xhr.abort, xhr);
    return promise;
    },

    I have problem with promise.abort. Do you have any idea?

    Reply

    • Gur Dotan

      June 14, 2014 at 7:11 am

      You should apply the `when` function to the jQuery object:
      $.when.apply($, …);

      But the real reason this doesn’t work for you is that you’re storing a jQuery Deferred object in the `xhr` variable, and not an actual jQuery XHR object (which is the returned value from all $.ajax and Backbone fetch calls). So effectively in your code, you will never have the `abort` function available.

      Reply

  3. Alexander Roussakov

    August 6, 2015 at 7:58 am

    Thanks, very helpfull

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

DOWNLOAD OUR RECENT REPORT

Join 8,283 other smart people who get email updates for free!

We don't spam!

Unsubscribe any time

Categories

LAST STEP - ENTER EMAIL TO GET THE REPORT



By proceeding, you agree with our terms and conditions and privacy policy.

LAST STEP - ENTER EMAIL TO GET THE REPORT



By proceeding, you agree with our terms and conditions and privacy policy.

LAST STEP - ENTER EMAIL TO GET THE REPORT



By proceeding, you agree with our terms and conditions and privacy policy.

LAST STEP - ENTER EMAIL TO GET THE REPORT



By proceeding, you agree with our terms and conditions and privacy policy.

LAST STEP - ENTER EMAIL TO GET THE REPORT



By proceeding, you agree with our terms and conditions and privacy policy.

LAST STEP - ENTER EMAIL TO GET THE REPORT



By proceeding, you agree with our terms and conditions and privacy policy.

LAST STEP - ENTER EMAIL TO GET THE REPORT



By proceeding, you agree with our terms and conditions and privacy policy.

LAST STEP - ENTER EMAIL TO GET THE REPORT



By proceeding, you agree with our terms and conditions and privacy policy.

LAST STEP - ENTER EMAIL TO GET THE REPORT



By proceeding, you agree with our terms and conditions and privacy policy.

LAST STEP - ENTER EMAIL TO GET THE REPORT



By proceeding, you agree with our terms and conditions and privacy policy.

Join 8,283 other smart people who get email updates for free!

We don't spam!

Unsubscribe any time