Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jquery deferred turn failure into success

So in using jQuery deferreds and $.when to load many objects in parallel.

$.when(
  a.ajax(), b.ajax(), c.ajax()
).then(
  //do something when all are complete
  complete();
);

Now, b.ajax() will sometimes fail, but I dont actually care. I just want to wait until all the calls have completed before calling complete().

Unfortunately, as soon as b fails, the when() rejects, and never fires the then()callback. This is AFAIK expected behaviour for $.when(), however dosent suit me in this case.

I effectively want a way to say:

$.when(
  a.ajax(), b.ajax().fail(return success), c.ajax()
).then(...)

Or perhaps there is a different way to use when(), or a more suitable construct?

like image 526
dalyons Avatar asked Jun 06 '11 23:06

dalyons


2 Answers

If you want to capture the failure of a promise and convert that to a success, you can use the failFilter of then to return a resolved promise, like so:

deferredCall.then(function(answer) { 
   // this is success. you might transform the answer here.
   return transformed;
}, function() {
   // this is a fail. you might resolve the fail with an empty object.
   return $.Deferred().resolve({}).promise();
});

Doing this will ensure that the chain can continue past the failure unbroken.

So, for your example, you might do this:

$.when([
   a.ajax(),
   b.ajax().then(function(answer) { 
       return answer; 
   }, function() {
       return $.Deferred().resolve({}).promise();
   }),
   c.ajax()
]).then(function(results) {
    // etc.
});

Example 2: In my applications, I sometimes use then to get relational data for a particular entity and allow for the possibility of a 404 to indicate that no such relationship exists:

getEntity(id).then(function(entity) {
    return getAssociation(id).then(function(association) {
        entity.association = association;
        return entity;
    }, function() {
        entity.association = null;
        return $.Deferred().resolve(entity).promise();
    });
}).done(function(entity) {
    // etc.
});

Note, older answers suggest using the pipe method. This method is deprecated as of jQuery 1.8.

like image 183
Griffin Avatar answered Oct 30 '22 06:10

Griffin


Here is something better than hacking a failure into a success.

Little known fact, $.when() will execute the then() callback immediately if any one of the parameters fails. It's by design. To quote the documentation:

http://api.jquery.com/jQuery.when/

In the multiple-Deferreds case where one of the Deferreds is rejected, jQuery.when immediately fires the failCallbacks for its master Deferred. Note that some of the Deferreds may still be unresolved at that point. If you need to perform additional processing for this case, such as canceling any unfinished ajax requests, you can keep references to the underlying jqXHR objects in a closure and inspect/cancel them in the failCallback.

There's actually no built-in way of waiting until all of them are finished regardless of their success/failure status.

So, I built a $.whenAll() for you :) It always waits until all of them resolve, one way or the other:

http://jsfiddle.net/InfinitiesLoop/yQsYK/

like image 41
InfinitiesLoop Avatar answered Oct 30 '22 07:10

InfinitiesLoop