0

If I have a 2 nested deferred objects and I'm waiting on both to finish, how can I ensure that their then completes before the outer then triggers?

$.when($.ajax({
    method: 'PUT',
    url: someURL,
    data: {
        // My data
    },
    callback: function(r) {

        var deferred1 = $.Deferred();
        var deferred2 = $.Deferred();

        $.ajax({
            method: 'PUT',
            url: url1,
            data: {
                // My data
            }
        }).complete(deferred1.resolve);

        $.ajax({
            method: 'PUT',
            url: url2,
            data: {
                // My data
            }
        }).complete(deferred2.resolve);

        $.when(deferred1, deferred2).then(function() {
            self.parent.container.dialog('close').remove();
            self.parent.configurator.container.dialog('close').remove();
        });
    },
})).then(function() {
    // Some work; e.g. close a loading spinner
});

With the above code, immediately as deferred1 and deferred2 are resolved, the outer then is called before the inner then

Thelonias
  • 2,918
  • 3
  • 29
  • 63
  • You need to stop and read a tutorial instead of continuing to try to muddle forward. [This is a good one](http://joseoncode.com/2011/09/26/a-walkthrough-jquery-deferred-and-promise/). Your code is not really salvageable, every use of promises here is incorrect. – user229044 Feb 05 '16 at 02:27

1 Answers1

2

Promises aren't magical, they can't just know when "work is done", the only way they can check for completion is through return value. If you want to chain promises you must return from your then chains.

In this example you're not returning from the then call, so it doesn't wait.

In addition - you don't need to wrap regular promises with $.when, what $.when does is convert one or more maybe promises into a promise for their values.

$.ajax({
    method: 'PUT',
    url: someURL,
    data: {
        // My data
    },
}).then(function(result){ 
    var p1 = $.ajax({ // $.ajax already returns a promise
        method: 'PUT',
        url: url1,
        data: {
            // My data
        }
    })
    var p2 = $.ajax({
        method: 'PUT',
        url: url2,
        data: {
            // My data
        }
    });
    return $.when(p1, p2);
}).then(function() {
    self.parent.container.dialog('close').remove();
    self.parent.configurator.container.dialog('close').remove();
}).then(function() {
    // some action
});
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • The reason I explicitly created 2 deferred objects inside the callback is to work around the fact that jQuery doesn't have a sort of "whenAll" feature. By doing it the way I am, I'm guaranteed that both of the requests in the callback are resolved before I close the dialogs. – Thelonias Feb 04 '16 at 22:37
  • 1
    You're guaranteed here as well. `$.when` is exactly a "whenAll" feature. By creating explicit deferred you were just [creating it explicitly instead of chaining](http://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it). – Benjamin Gruenbaum Feb 04 '16 at 22:38
  • Suppose I had success callbacks for each of p1 & p2, and p1 failed before p2 finished. Wouldn't that immediately call the first `then`? If so, that's not how I need it to work. – Thelonias Feb 04 '16 at 22:43
  • 1
    No, it would not, you can test these things easily in jsfiddle by the way. – Benjamin Gruenbaum Feb 04 '16 at 22:54
  • Correct me if I'm wrong, but if I wanted to wrap each inner ajax call with a conditional (I don't always want to call it), I'd need to keep the "deferred" variables I've got and either set it to the return value of the ajax call or explicitly resolve it in the conditional's `else` – Thelonias Feb 04 '16 at 23:07
  • Right, you'd do conditional branching through regular conditionals like `if/else` and return promises directly in the branches. In any case I wouldn't create an explicit deferred object. – Benjamin Gruenbaum Feb 04 '16 at 23:09
  • Hm, I guess I don't follow. In the `else`, how would I return a promise? I'm not doing anything in the else other than just NOT making the ajax call (so I resolved the deferred object). If it's not too much of a bother, would you mind updating your example showing a conditional around one of the inner ajax calls and what to do in the `else` case? – Thelonias Feb 04 '16 at 23:14