Search code examples
javascriptjquerypromisedeferred

Deferred Promise - Run Functions One by One after each completes


I have 3 functions that make aync calls to backend systems : fnOne, fnTwo, fnThree. I know I am not doing it correctly, but cannot figure out the right way.

I am starting off by trying to call them one by after each completes:

However, fnOne already has a deferred object inside of it and when that is resolved it is resolving my promise.

$.when(oController._fnOne()).done(function(){
    console.log("fn one complete");
});

functions

_fnOne: function(){

    var oController = this;

 //when the deferred is done in _checkInstanceStatus, it is resolving the above, rather than the pDeferred in this function resolving the above.
$.when(oController._checkInstanceStatus(oParams)).done(function(oStatusData) {
        //does some stuff 
        return $.Deferred(function() {
            var pDeferred = this;
            //does a call for data and upon success i will resolve ..
            pDeferred.resolve();
        });
    });
}

other function

_checkInstanceStatus: function(oParams){
            return $.Deferred(function() {
                var pDeffered = this;
                //does a call for data and upon success i will resolve...
                pDeffered.resolve(data);

                });
            });
        },

Then the plan is to try and chain them, so they run one by one, like so:

$.when(oController._fnOne())
.then(oController._fnTwo())
.then(oController._fnThree())
.done(function(){
    console.log("all complete!");
});

Solution

  • A couple of things:

    1. There's no need to use $.when if you're passing it a single Deferred.

    2. fnOne (and presumably the others) needs to return a promise/Deferred so you know when it completes. Since it's using _checkInstanceStatus, which returns a Deferred, it can do that by using then:

    So fnOne might look like this:

    fnOne: function() {
        var oController = this;
        return oController.__checkInstanceStatus(oParams).then(function(oStatusData) {
            var pDeferred = $.Deferred();
            callAmazon(function() {
                if (/*successful*/) {
                    pDeferred.resolve();
                } else {
                    pDeferred.reject();
                }
            });
            return pDeferred.promise(); // Or just the Deferred if you like, but
                                        // normally you want to share promises, not
                                        // Deferred objects
        });
    }
    

    Notice how it returns the result of calling then, which is a promise created by calling then. That promise will resolve based on the promise you're returning from the then handler.

    You'd follow the same pattern with the other functions.

    To chain them one after another, you'd do this:

    oController._fnOne()
    .then(oController._fnTwo)        // Notice no () on that, we're passing the
    .then(oController._fnThree)      // function in, not calling it. Same on this line.
    .then(function() {
        console.log("all complete!");
    })
    .catch(function(error) {
        // Do something about the error
    });
    

    (I'm assuming _fnOne and fnOne are the same function.)

    I'm assuming a relatively recent version of jQuery that has Promises support in Deferreds.


    Side note: I'd switch to using native Promises (with a polyfill if necessary to support old browsers) rather than using jQuery's Deferred, which has...well, it has a lot of history and the API has gotten cumbersome. :-)

    With native promises:

    fnOne: function() {
        var oController = this;
        return oController.__checkInstanceStatus(oParams).then(function(oStatusData) {
            return new Promise(function(resolve, reject) {
                callAmazon(function() {
                    if (/*successful*/) {
                        resolve();
                    } else {
                        reject();
                    }
                });
            });
        });
    }
    

    Usage is the same (because I used Promise APIs that have been added to Deferred in the first example).