Amir
Amir

Reputation: 4770

How can I fire JQuery functions sequentially with deferred()?

I am really having trouble wrapping my head around the deferred() method inside jquery. I've spent several hours reading the documentation, but I still don't fully understand what I'm doing.

My basic problem is, I have a series of functions (not ajax calls) that I want to run in sequence, but stop all processes if there is an error in any of them.

Here is how far I've gotten (I've stripped out a bunch of unneeded code and just left the basic idea)

//The module  var myModule = (function() {

//Defaults
var vOne;
var VTwo;
var deferred = $.Deferred();

//Private method
var _myPrivateFunction1 = function(userID) {
    if(userID >= 10) {
        //All is good, set var vOne to true and run next function
        vOne = true;
        return deferred.promise();
    } else {
        //User is not allowed, stop everything else and show the error message
        return deferred.reject();
    }
}

var _myPrivateFunction2 = function() {
    if(vOne === true) {
        //Ok we can keep going
        return deferred.promise();
    } else {
        //again stop everything and throw error
        return deferred.reject();
    }
};

var _myPrivateFunction3 = function(element) {
    //...and so on
};


var _errorMsgFunction = function(msg) {
    $.log("There was an error: " + msg);
    return false;
};


//Public method
var myPublicFunction = function(element,call) {
    //element is jquery object passed through user "click" event
    var userID = element.data('id')
    var userAction = element.data('action');

    //Now... i want to fire _myPrivateFunction1, _myPrivateFunction2, _myPrivateFunction3 in sequence and STOP all processes, and run 
    // _errorMsgFunction if there is an error in any of them.
    //This is how far I've gotten...

    _myPrivateFunction1(userID).then(_myPrivateFunction2(userAction), _errorMsgFunction("Error in _myPrivateFunction2")).then(_myPrivateFunction3(element),_errorMsgFunction("Error in _myPrivateFunction3")).fail(_errorMsgFunction("Error in _myPrivateFunction1"));

};

// Public API
return {
    myPublicFunction: myPublicFunction
};
})();

So right now I keep getting "Error in _myPrivateFunction2" (I'm forcing this error for testing purposes), but the other functions after continue to fire...They don't stop. What am I missing here?

Upvotes: 0

Views: 77

Answers (2)

Henrique Barcelos
Henrique Barcelos

Reputation: 7900

You cannot share deferred objects. You should create a different promise from a deferred for each function.

Here is some very simple example, using sycnhronus functions for the sake of simplicity, although promises are meant to be used with asynchronous functions:

var func1 = function(arg){
    var dfd = jQuery.Deferred();
    if (arg === 0) {
        dfd.resolve('func1 Ok');
    } else {
        dfd.reject('func1 arg != 0');
    }
    return dfd.promise();
}

var func2 = function(arg){
    var dfd = jQuery.Deferred();
    if (arg === 0) {
        dfd.resolve('func2 Ok');
    } else {
        dfd.reject('func2 arg != 0');
    }
    return dfd.promise();
}

var func3 = function(arg){
    var dfd = jQuery.Deferred();
    if (arg === 0) {
        dfd.resolve('func3 Ok');
    } else {
        dfd.reject('func3 arg != 0');
    }
    return dfd.promise();
}

If the functions does not depend on other to do their processing, we can do it in parallel using jQuery.when

// Parallel processing

jQuery.when(func1(1), func2(0), func3(0)).then(function(res1, res2, res3){
    console.log(res1, res2, res3);
}).fail(function(reason){
    console.error(reason); // will fail with reason func1 arg != 0
});

If it is a sequence processing (as I undertood your problem is), you should do:

// Sequential processing

func1(0).then(function(res1){
    return func2(res1);
}).then(function(res2){
    return func3(res2);
}).then(function(res3){
    // everything ran ok, so do what you have to do...
}).fail(function(reason){
    console.log(reason);
});

The code above will fail with reason:

> func2 arg != 0

If you have mixed parallel and sequential processing to do, then you should mix both approaches.


Disclaimer

As in my example, if func1 or func2 have side effects, they will not be undone within fail() by themselves.

The best practice is to only have side effects when you are absolutely sure that everything went ok, that is, inside the last then() call.

Upvotes: 2

lanan
lanan

Reputation: 2752

You will need a separate $.deferred() inside each of your functions, because you want to return unique promise for each function.

//Private method
var _myPrivateFunction1 = function(userID) {
    var deferred = $.Deferred();
    if(userID >= 10) {
        //All is good, set var vOne to true and run next function
        vOne = true;
        deferred.resolve();
    } else {
        //User is not allowed, stop everything else and show the error message
        deferred.reject();
    }
    return deferred.promise();
}

Then your public function should work.

Upvotes: 1

Related Questions