0

I have a series of asynchronous actions whose responses must be synchronized in order to run a final action. I'm using The Deferred object and the when() and done() methods to achieve this, but for some reason, the final action within done() is always executed when the first resolve() of the responses is invoked.

This is the code I have, cut a bit short to make it clearer:

       // Central function that creates a Deferred object and returns a
       // Promise from it. when the passed request is done, 
       // the Deferred is resolved
       var getData = function(url, successFunction) {
            var deferred = $.Deferred();
            $.ajax({
                url: url,
                method: 'get',
                dataType: 'json'
            }).done(function(p) {
                successFunction(p);
                deferred.resolve(p);
            }).fail(function(p){
                deferred.reject(p);
            });
            return deferred.promise();
        };

        // Success actions to be called for each asynchronous request

        var populateDestinations = function(data) {
            // synchronous actions on DOM
        };

        var populateTaxes = function(data) {
            // synchronous actions on DOM
        };

        var populatePayment = function(data) {
            // synchronous actions on DOM
        };


        // Actions that return a Promise to control the resolution of 
        // all the deferred objects

        var getCustomerDestinations = function(customerId) {
            var url = $modal.data('url_destinations') + customerId;
            return getData(url, populateDestinations);
        };

        var getCustomerTaxes = function(customerId) {
            var url = $modal.data('url_taxes') + customerId;
            return getData(url, populateTaxes);
        };

        var getCustomerPayment = function(customerId) {
            var url = $modal.data('url_payment') + customerId;
            return getData(url, populatePayment);
        };

        var populateFields = function() {
            // final actions
        };


        $.when(getCustomerDestinations(customer_id),
                getCustomerTaxes(customer_id),
                getCustomerPayment(customer_id))
        .done(function(){

             populateFields();

        });

populateFields() is being called whenever one of the "promised" functions is resolved, not when all of them are resolved.

Any idea what I'm doing wrong? Maybe I haven't yet grasped the concept of the Deferred object.

luis.ap.uyen
  • 1,314
  • 1
  • 11
  • 29
  • 1
    You are missing closing parenthesis in important places. Please add them (show us code that is not a syntax error). – Bergi May 18 '16 at 09:17
  • 1
    Avoid the [deferred antipattern](http://stackoverflow.com/q/23803743/1048572)! It should be just `function getData(url, fn) { return $.getJSON(url).then(fn); }` – Bergi May 18 '16 at 09:19
  • 1
    actually you dont need to create a deferred object to get the promise , instead just return `$.ajax()`, which will return a promise object by default. so remove all deferred objects and just return from `$.ajax()` – dreamweiver May 18 '16 at 09:25
  • I've just added the missing parenthesis. – luis.ap.uyen May 18 '16 at 09:58
  • 1
    The real error was that, despite it's not present in my cut short example because there are more actions in the when() method, this was not enclosing all the actions. I added its closing parenthesis before. So it was a logic error. I've checked that this way, both my initial implementation with Deferreds, and yours just returning the $.ajax promise, will work. However, I'll take your advice and use you implementation, which is simpler. Thanks both of you! – luis.ap.uyen May 18 '16 at 10:05
  • good to know, now can we close this question. – dreamweiver May 18 '16 at 11:35

1 Answers1

2

you really dont need to use any deferred objects to track ajax calls, instead you can just use the promise object returned from $.ajax within $.when().

JQUERY CODE:

var getData = function(url, successFunction) {
        return $.ajax({
            url: url,
            method: 'get',
            dataType: 'json'
        }).then(function(p) {
            successFunction(p);
        },function(p){
            //process error using error callback, 
            //just like success callbacks
        });
    };

I order to process individual ajax calls you can use .then() instead of .done() & .fail(), because these will not return any promise object unlike .then() to track the same in .when().

Rest of your code will work as it is.

What jQuery forum says:

As of jQuery 1.8, the deferred.then() method returns a new promise that can filter the status and values of a deferred through a function, replacing the now-deprecated deferred.pipe() method. The doneFilter and failFilter functions filter the original deferred's resolved / rejected status and values. The progressFilter function filters any calls to the original deferred's notify or notifyWith methods. These filter functions can return a new value to be passed along to the promise's .done() or .fail() callbacks, or they can return another observable object (Deferred, Promise, etc) which will pass its resolved / rejected status and values to the promise's callbacks.

refence links :

dreamweiver
  • 6,002
  • 2
  • 24
  • 39
  • You should always use `then` instead of `done` and `fail` (unless you know what you are doing) – Bergi May 18 '16 at 11:52
  • usage is `then()` is nothing but using `done` & `fail` underneath it , so it really doesnt make a difference. here is the statement from jquery frorum for `then`, **"jqXHR.then():Incorporates the functionality of the .done() and .fail() methods, allowing (as of jQuery 1.8) the underlying Promise to be manipulated. Refer to deferred.then() for implementation details."** – dreamweiver May 18 '16 at 11:58
  • It *does* make a difference (in the return value), and that's why it should be used by default. `then` is more than just `done` and `fail`. You should get used to it. – Bergi May 18 '16 at 17:16
  • Hmmm, you got any article on this, I haven't seen any difference though. – dreamweiver May 18 '16 at 17:45
  • @Bergi: thanks mate, now i understood what you meant. will updated answer as well :) – dreamweiver May 19 '16 at 08:40
  • @dreamweiver Thanks for your help. Although I'm still not sure about the difference between then() and done()/fail() – luis.ap.uyen May 19 '16 at 11:17
  • 1
    prior of jquery 1.8, `.then()` was same as `done()` or `fail()` but after jQuery 1.8 `.then()` returned more than just a promise object. check this SO answer for more information.http://stackoverflow.com/a/5436869/1677272 . – dreamweiver May 19 '16 at 11:44
  • Even though you code may work either way, i would suggest you to go with `.then()` , just to be future proof. – dreamweiver May 19 '16 at 11:53