Antonio Laguna
Antonio Laguna

Reputation: 9292

How to return promise that will resolve with the result of several functions?

I'm developing a jQuery plugin, which pretends to translate elements on the page, automatically depending on the user's browser language. Translations will be stored in .json files.

When you call the plugin, you pass a package name (or an Array of them) and then it will try to load the language file in the following way:

So, in the plugin I have this:

$.fn.Translator = function(pkg, options){
    Translator.initialize(pkg, options).done(function(){
        return this.each(Translator.translate);
     });            
};

So, somewhere in my initialize function, I have this:

loadLanguages : function(){
    $.each(self.options.packages,function(i, pkg){

    });
}

Which will call this function :

getLanguage : function(pkg, language){
    var self = this, url;
    if (self.options.path)
        url = self.options.path + '/';
    url += [pkg, language].join('-');

    return $.ajax ({
        url : url,
        dataType : "json",
        cache : self.options.cache
    });
}

The problem is that since that function will be called probably multiple times, I don't know how to make initialize to return a promise which will be resolved once ALL of the functions have been called.

Upvotes: 0

Views: 1233

Answers (2)

Julian Aubourg
Julian Aubourg

Reputation: 11436

I know you accepted an answer already but there is a much simpler way: use $.when.

function loadLanguages () {
    return $.when.apply( $, $.map( self.options.packages, function( pkg ) {
        return getLanguage(pkg, language);
    }) );
}

Upvotes: 2

dennisg
dennisg

Reputation: 4368

To solve this problem you can create a dummy Deferrer (baseDfr) and return the dummy as result of initialize(). The idea here is to call baseDfr.resolve() when all getLanguage() calls are done. We keep track of the number of getLanguage() calls that are done using a counter. When the counter reaches 0, we call baseDfr.resolve(). We know when a getLanguage() call is done using the then() or done() method on the returned deferrer by $.ajax .

The code to solve this can be found below. Also, a working example (on some divs) can be found here: http://jsfiddle.net/c5NBr/. Open your console to see the messages appear in correct order.

var packages = [];
var count = 0;
var baseDfr = $.Deferred();
var language = "en";

function resolveFunction () {
    // By using this approach it is possible to pass a 
    // parameter to this resolve function.
    return function(){
        if ( !(--count) ) {
            // Until count is 0, we won't resolve the base
            // deferrer object.
            // As long as this isn't called, the function
            // done() of initialize won't be called either.
            baseDfr.resolve();
        }     
    };
}

function getLanguage (pkg, language){
    var self = this, url;
    if (self.options.path)
        url = self.options.path + '/';
    url += [pkg, language].join('-');

    return $.ajax ({
        url : url,
        dataType : "json",
        cache : self.options.cache
    }).promise();
}

function loadLanguages () {
    $.each(self.options.packages,function(i, pkg){
        getLanguage(pkg, language).then(resolveFunction());
    });

    return baseDfr.promise();                
}

function initialize(options) {
    packages = options.packages;
    count = options.packages.length;    

    return loadLanguages();
}

var options =  {
    packages : $('div')          
};

initialize(options).done(function(){
    // This will only be called when baseDfr.resolve() is called.
    console.log("Fired all getLanguage().");
});

Upvotes: 0

Related Questions