Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery when/then/fail with concurrent ajax requests: Which request failed?

Imagine a scenario where we want to do something after the concurrent requests for 'foo' and 'bar' have completed successfully, or report an error if one or both of them fails:

$.when($.getJSON('foo'), $.getJSON('bar'))
  .then(function(foo, bar) {
    console.log( 'I fire if BOTH requests are successful!' );
  })
  .fail(function() {
    console.log( 'I fire if one or more requests failed.' );
  });

How can I determine if 1) the request for 'foo' failed, or 2) the request for 'bar' failed, or 3) if both failed?

like image 573
Javier Avatar asked Apr 26 '11 18:04

Javier


2 Answers

Simply add a fail call to each promise that is returned from $.getJSON:

function make_error_handler(msg) {
    return function() { console.log(msg); };
}

$.when($.getJSON('foo').fail(make_error_handler("foo failed"))
            , $.getJSON('bar').fail(make_error_handler("bar failed")))
  .then(function(foo, bar) {
    console.log( 'I fire if BOTH requests are successful!' );
  })
  .fail(function() {
    console.log( 'I fire if one or more requests failed.' );
  });

If you need more fine-grained control you can overload $.getJSON to return additional information to the fail function -- see jQuery's documentation on deferred.rejectWith

like image 55
Sean Vieira Avatar answered Nov 03 '22 03:11

Sean Vieira


From the documentation on 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.

In other words, you are supposed to keep the references to each request, and check it manually yourself, if you need to.

The difficulty is that when the fail callback is called, any number of deferred's can still be outstanding, thus at that state they have neither failed nor succeeded.

I wish the documentation explained a bit better as to exactly what is passed to the fail handler in this case. Judging from the source code though, it seems that the arguments received by the fail handlers depend on which Deferred fails first:

args[ i ].promise().then( resolveFunc(i), deferred.reject );

I'm no expert on jQuery internals, but I think this simply lets each of your Deferred's call the "master" Deferred's reject method with whatever arguments it would normally pass to its handlers. Not ideal, if you ask me... but then I could be misreading this.

I do wonder if they meant this though:

args[ i ].promise().then( resolveFunc(i), func() { deferred.reject(args); } );
like image 27
Roman Starkov Avatar answered Nov 03 '22 05:11

Roman Starkov