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?
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?
Share Improve this question edited Jun 4, 2011 at 14:46 lonesomeday 238k53 gold badges327 silver badges328 bronze badges asked Apr 26, 2011 at 18:45 JavierJavier 1011 gold badge1 silver badge4 bronze badges 3- Have you tried just doing one at a time? – locrizak Commented Apr 26, 2011 at 18:48
- @locrizak - Imagine an application that requests 'foo' and 'bar' concurrently, but needs both to return successfully before processing them. – Javier Commented Apr 26, 2011 at 18:55
- why not make both foo and bar return true or false depending on their success or failure, call both functions and save their returns in separate variables? that way you can require both to be true and if not, check which of the two was false. – eagerMoose Commented Apr 26, 2011 at 19:03
3 Answers
Reset to default 11Simply 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
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); } );
You could call .isResolved()
to check whether a request did actually complete, but the problem with .when()
is that it'll fail as soon as either request fails, so you can't reliably tell whether the other request might be about to fail.
I believe the answer I just posted to a similar question will resolve (no pun intended) this requirement, by creating an extra $.Deferred
for each AJAX query, which allows you to wait until you know the actual outcome of both AJAX calls.