Oriol
Oriol

Reputation: 12740

Nesting deferred calls

Basically this is what I'm trying to do in pseudo-code:

Fetch AsyncResultA
  If Success
    Fetch 
      AsyncResultB
      AsyncResultC
    Either Success/Fails
      Display any successful results
  If Fails
    Display error

And this is what I've done in jQuery but it doesn't work:

        $.when (

          fetchA()

        ).then (function (data, textStatus, jqXHR) {

            return {
              xhrObject1: [data, textStatus, jqXHR],
              xhrObject2: fetchB(),
              xhrObject3: fetchC(),
              xhrObject4: fetchD()
            }

          }, fail

        ).always( displayResults );

Notes:

All fetch functions are AJAX calls.

displayResults would get the successful fetched data and displays it all at once. So as long as fetchA succeeds, something would display.

fail would only get called when fetchA fails, thus canceling any further fetches.

Upvotes: 2

Views: 1317

Answers (1)

Felix Kling
Felix Kling

Reputation: 817238

It doesn't work because you are not returning a promise from the .then callback, but an ordinary object. That has the effect that displayResults is basically called on the original promise returned by fetchA instead. The relevant part from the documentation (emphasis mine):

As of jQuery 1.8, the deferred.then() method returns a new promise [...]. 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.

Use $.when and return its return value:

fetchA().then(function () {
    return $.when([].slice.call(arguments), fetchB(), fetchC(), fetchD());
}, fail).always( displayResults );

$.when returns a promise that is resolved when all the promises passed to itself are resolved (or rejected). Therefore is no reason to use $.when if you only pass a single argument to it.

If you don't want to execute displayResults when fetchA fails, you can bind the fail handler separately:

fetchA().fail(fail).then(function () {
    return $.when([].slice.call(arguments), fetchB(), fetchC(), fetchD());
}).always( displayResults );

Upvotes: 3

Related Questions