2

Suppose I able to get ($.ajax), process (process_*) and save (store_* =) data A and B independently and already have API:

var store_A;
function A() {
  $.ajax({url: "/getA"}).done(function(data) {
     store_A = process_A(data);
}); }

var store_B;
function B() {
  $.ajax({url: "/getB"}).done(function(data) {
     store_B = process_B(data);
}); }

I have a function C() that able to combine store_A and store_B into something:

var store_C;
function C() {
   store_C = process_C(store_A, store_B);
}

Suppose that A(), B() and C() is public API and all other stuff is internals and actually complex code (so for example I can't chain both $.ajax directly).

I want to refactor above code to new API by using jQuery Deferred API so I can request any case:

case1: after(A).do(C)
case1: after(B).do(C)
case2: afterParallelJobsFinished(A, B).do(C)

and be sure that store_A or store_B updated as requested and store_C updates only after update of one or both of A/B as requiested.


Imaging that we have webapp where user manage set of income and expenses.

On page loading we get incomes and expenses from different data-sources (that is $.ajax) in parallel, render view and store data (usually that are intermixed process_* / store_* =) and show total = SUM(incomes) - SUM(expenses) when all data arrived.

That is case2.

When user edit expenses and request partial page update we in case1 because we need only load/render/store expanses to get correct total = SUM(incomes) - SUM(expenses).

gavenkoa
  • 45,285
  • 19
  • 251
  • 303
  • You can use $.when but you need to store promise in store_X. – jcubic Dec 28 '15 at 20:04
  • From two independent `$.ajax()` I get two promises. How can I combine them? I want to preserve ability to run `A()` or `B()` or `C()` independently. – gavenkoa Dec 28 '15 at 20:06

3 Answers3

3

$.when() can take an arbitrary number of deferred objects. It can even take a dynamic number if you don't know how many calls you will make.

$.when(callA(), callB()).done(function() {
 // Do stuff here when both calls are done
});

function callA() {
  return $.ajax({
    url: ...,
    success: function() {
      // Do stuff here when callA is done
    }
  });
}

function callB() {
  return $.ajax({
    url: ...,
    success: function() {
      // Do stuff here when callB is done
    }
  });
}
Community
  • 1
  • 1
Matthew Herbst
  • 29,477
  • 23
  • 85
  • 128
  • Nice! Can I use `return $.ajax().done(...)` syntax instead of `return $.ajax({success: ...})` ? Are they equivalent? – gavenkoa Dec 28 '15 at 20:10
  • 1
    No, not the same. You don't want `done` being called when one of them is finished - only when they all finish. – Matthew Herbst Dec 28 '15 at 20:11
  • Thanks. I think that my question mostly arose because of `done` vs `success` misunderstanding. – gavenkoa Dec 28 '15 at 20:14
  • 1
    Very true. The docs don't make it at all clear how they work together. You can use `done` in replace of `success` in certain situations, but for the above use case, I recommend using both in the places as I have shown. – Matthew Herbst Dec 28 '15 at 20:15
  • I would not use `done` but `then`. That way it is still returning a promise. like this: `return $.ajax().then(...)`. The `$.when()` will be fulfilled when each of the `then` functions from callA and callB are completed. Personally I think its a little ugly to mix `$.Deferred` logic with the success callback. "What happens first? What is actually returned? Do they both get passed the result? Does success chain?" All valid questions that can be avoided by sticking to just `$.Deferred` – thedarklord47 Dec 29 '15 at 20:48
2

This is a revision of Matthew Herbst's answer sticking solely to Promise logic. This avoids unnecessary confusion from mixing Promise logic and the ajax success: callback.

$.when(callA(), callB()).done(function() {
    // Do stuff here when both .then()'s are complete
});

function callA() {
    return $.ajax({
        url: ...,
    }).then(function(result){
        // Do something when callA finishes
        return result;
    });
}

function callB() {
    return $.ajax({
        url: ...,
    }).then(function(result){
        // Do something when callB finishes
        return result;
    });
}
thedarklord47
  • 3,183
  • 3
  • 26
  • 55
1

Now when I become familiar with jQuery.Deffered API I want to add some more self expressed examples:

function callA() {
  return $.ajax({ url: ... })
    .done(function(data) { successA(data); })
    .fail(function(data) { failA(data); });
}

function callB() {
  var promise1 = $.ajax({ url: ... })
    .done(function(data) { succB_1(data); })
    .fail(function(data) { failB_1(data); });
  var promise2 = $.ajax({ url: ... })
    .done(function(data) { succB_2(data); })
    .fail(function(data) { failB_2(data); });
  return $.when(promise1, promise2)
    .done(function(data1, data2) { succCombined(data1, data2); })
    .fail(function(data1, data2) { failCombined(data1, data2); });
}

$.when(callA(), callB()).done(function() {
 // Do stuff here when both calls are done
});

Note that inside callA/callB I use data/data1/data2 because I know internal API agreement. In last lines I don't expect that callA/callB return something sensible. But if I add public API to callA/callB I can use:

$.when(callA(), callB()).done(function(resultA, resultB) {
 // Do stuff here when both calls are done
});
gavenkoa
  • 45,285
  • 19
  • 251
  • 303