Kevin Burke
Kevin Burke

Reputation: 64844

Make two jQuery AJAX calls and get context for both, even if one fails

I'd like two make two AJAX requests for data. One or both of the requests may fail. In that case I still would like to interact with the data from both requests (or the successful request).

If I do something like:

$.when($.get("page1"), $.get("page2")).then(function(a1, a2) {

})

The then function will only get called if both requests succeed, so if one fails, I can't get any of the data from the successful request. If I use a failCallback for then, or use the always method, like this:

$.when($.get("page1"), $.get("page2")).then(function(a1, a2) {
    console.log("this is only called if both succeed.");
}, function(a1, a2, a3) {
    console.log("this is only called with a then() failure");
}).always(function(a1, a2, a3) {
    console.log("this will fire when always() does.");
});

The failCallback and always callback only report data on the failed request, so I can't get data out about the successful request. Similarly, using the done() deferred doesn't call if one of the requests fails. So there's a situation where if one request 404s, I can't get any data from the successful function.

I suppose I could decouple the deferreds, so they are not both in the when loop. However then I run into issues with ensuring that both finish before proceeding.

Upvotes: 0

Views: 348

Answers (2)

Beetroot-Beetroot
Beetroot-Beetroot

Reputation: 18078

Utility method $.get() only provides for a success callback but the low level $.ajax() provides for success, error and always callbacks (or their .done(), .fail(), .then() equivalents). By resolving a Deferred of your own (for each wouldbe .get()), regardless of whether the ajax succeeds or fails, you obtain the control you need.

There must be many ways to formulate the code; here's one, involving an adapter function liberalGet(), which returns a liberalised promise that is always resolved and never rejected regardless of the $.ajax() outcome.

function liberalGet(url) {
    var dfrd = $.Deferred();
    $.ajax({
        url: url
    }).then(dfrd.resolve);
    return dfrd.promise();
}

$.when(liberalGet("page1"), liberalGet("page2")).done(function(a1, a2) {
    var textStatus = [ a1[1], a2[1] ];//"success", "notmodified", "error", "timeout", "abort", or "parsererror".
    console.log(textStatus.join(', '));
});

As you can see, the arguments a1[1], a2[1] give you access to the textStatus of each ajax response. a1[0], a2[0] give you access to the jqXHR objects (and their properties). Thus you can loop and branch inside the handler as necessary for your application.

Upvotes: 0

bokonic
bokonic

Reputation: 1771

Here's an option (sorry to not use the jQuery deferred tools)

var NUM_CALLS = 2, count = 0, results = [], errors = [];
function callback(err, data) {
  count++;

  if (err){
    errors.push(err);
  }

  if (data){
    results.push(data);
  }

  if (count === NUM_CALLS) {
    done(errors,results);
  }
}


function ajax(url) {
  $.ajax({
    type: 'GET',
    url: url,
    success: function(){ return callback(null, arguments); },
    error: function(){ return callback(arguments, null)
  });
}

ajax('page1');
ajax('page2');

// now you have the errors and results
function done(errors, results) {

}

Upvotes: 1

Related Questions