3

So I have this pretty simple use-case:

init: function() {
    app.products = new app.Collections.Products();
    app.categories = new app.Collections.Categories();

    var collections = [app.products, app.categories];
    var complete = _.invoke(collections, 'fetch');

    $.when(null, complete).then(function(){
       // fetched, but no models
    });
};

This works fine from an XHR point of view, but inside the callback, the Models for the respective collections haven't been created yet. Is there a way to also look out for the "reset" event from the "fetch" in this promise-based solution?

benhowdle89
  • 36,900
  • 69
  • 202
  • 331
  • The models should have been created by the time a reset event has been triggered... it doesn't look like you're binding to the reset event here – Cory Danielson Mar 31 '14 at 15:28
  • 1
    @BenjaminGruenbaum I disagree with the duplicate. Some possible overlap, maybe, as your recent edit on your answer demonstrates, but a duplicate is really pushing it. – nikoshr Mar 31 '14 at 15:37
  • @nikoshr his issue is that he does not know how to promisify an API. Even before the recent edit I had a `.onload` case which is _practically the same thing_ - I made the edit "just in case". This question is about turning a callback API (backbone sync events) to a promisified one. These repeat every now and then. There is nothing specific to backbone, or his case here. It is the exact same issue. – Benjamin Gruenbaum Mar 31 '14 at 15:40

2 Answers2

0

You can build your own deferred to coordinate your calls. For example, you could add this function on the prototypes of your collections :

function promiseSync() {
    var dfd = $.Deferred();
    this.once('sync', function() {
        dfd.resolve();
    });
    return dfd.promise();
}

app.Collections.Products.prototype.promiseSync = promiseSync;
app.Collections.Categories.prototype.promiseSync = promiseSync;

And then, exactly as you would do for the requests, combine the deferred:

var syncs = _.invoke(collections, 'promiseSync');
$.when.apply(null, syncs).then(function(){
   console.log('syncs');
});

And a demo http://jsfiddle.net/nikoshr/Xb9Mk/

Note the usage of $.when.apply(null, array). $.when(null, array) would resolve immediately and that's probably why you don't see your models in your callback.

nikoshr
  • 32,926
  • 33
  • 91
  • 105
0

With this code, you'll have your products and categories collections fetched with their models populated.

The deferredFetch function sets up the deferred object and resolves it once the collection is populated. By calling fetch with the {reset: true} option, Backbone will populate the collection and trigger one reset event instead of multiple add events.

init just fetches both of the collections and then executes the commented code block. The products and categories are passed in because the deferred objects have been resolved with those objects in the reset listener.

function deferredFetch(collection) {
    var deferred = $.Deferred();
    collection.once('reset', function(collection) {
        deferred.resolve(collection);
    });
    collection.fetch({ reset: true });
    return deferred;
}

init: function() {
    app.products = new app.Collections.Products();
    app.categories = new app.Collections.Categories();

    $.when(deferredFetch(app.products), deferredFetch(app.categories))
     .then(function(products, categories) {
         // do yo thang
    });
}
Cory Danielson
  • 14,314
  • 3
  • 44
  • 51