Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Provide a default 'fail' method for a jQuery deferred object

I am writing a Javascript API client using jQuery. My top level request method looks like this:

function request(method, uri, params, proxies) {
  var deferred = $.Deferred();
  $.ajax({
    data: method == 'GET' ? params : JSON.stringify(params),
    contentType: 'application/json',
    dataType: 'json',
    url: api.root + uri,
    type: method,
    xhrFields: {
      withCredentials: true
    }
  }).done(function(body) {
    deferred.resolveWith(this, [body.data]);
  }).fail(function(xhr) {
    deferred.rejectWith(this, [xhr]);
  });

  return deferred.promise();
},

How can I have a default fail handler for my returned deferred? That is, if the deferred has no other handlers attached to it's fail condition, call a default handler.

I want to do this to have global exception handling in my application, except for the parts that have a specific handling (and will define their own fail handler on the deferred).

like image 259
jd. Avatar asked Sep 30 '13 18:09

jd.


People also ask

What is Deferred method in jQuery?

Deferred() method in JQuery is a function which returns the utility object with methods which can register multiple callbacks to queues. It calls the callback queues, and relay the success or failure state of any synchronous or asynchronous function.

Can jQuery be Deferred?

The jQuery. Deferred method can be passed an optional function, which is called just before the method returns and is passed the new deferred object as both the this object and as the first argument to the function. The called function can attach callbacks using deferred. then() , for example.

What is Deferred reject ()?

reject( [args ] )Returns: Deferred. Description: Reject a Deferred object and call any failCallbacks with the given args .

What is fail in jQuery?

fail() method in jQuery is used to add handlers which are to be called when the Deferred object is rejected. This method accepts one or more than one arguments, which can be either a function or an array of functions. Callbacks are executed in the same order they were added.


1 Answers

So, the cleanest way to use jQuery ajax in an API as of 2016 is to return a promise. But, you cannot determine whether a caller has attached an error handler or not to the promise.

So, what I'd suggest is that you just add a new argument to your function that tells the function to NOT apply the default error handling because the caller will take care of the error handling. And, I'd suggest you avoid the promise anti-pattern by just using the existing promise $.ajax() already returns rather than creating your own deferred:

function request(method, uri, params, proxies, skipDefaultErrorHandling){
    // default error handling will be used if nothing is passed
    // for skipDefaultErrorHandling
    var p = $.ajax({
        data: method=='GET'?params:JSON.stringify(params),
        contentType: 'application/json',
        dataType: 'json',
        url:  api.root + uri,
        type: method,
        xhrFields: {
            withCredentials: true
        }
    });
    if (!skipDefaultErrorHandling) {
       // apply default error handling
       p = p.then(null, function(jqXHR, textStatus, errorThrown) {
           // put here whatever you want the default error handling to be
           // then return the rejection with the various error parameters available
           return $.Deferred().reject([jqXHR, textStatus, errorThrown]);
       });
    }

    return p;
};

Then, the caller just decides whether to apply their own error handling or not:

request(...).then(function(data) {
    // success code here
});

Or, you can go with a non-promise failHandler callback that you pass in and your default error handling looks to see if that failHandler was passed in or not. This is hybrid of promises and callbacks and is not something I would normally choose to architect, but since your question asks for something that promises do not support, this is one of achieving that:

function request(method, uri, params, proxies, failHandler){
    // default error handling will be used if nothing is passed
    // for skipDefaultErrorHandling
    var p = $.ajax({
        data: method=='GET'?params:JSON.stringify(params),
        contentType: 'application/json',
        dataType: 'json',
        url:  api.root + uri,
        type: method,
        xhrFields: {
            withCredentials: true
        }
    });
    // apply default error handling
    p = p.then(null, function(jqXHR, textStatus, errorThrown) {
       if (failHandler) {
           // call passed in error handling
           failHandler.apply(this, arguments);
       } else {
           // do your default error handling here
       }
       // then keep the promise rejected so the caller doesn't think it
       // succeeded when it actually failed
       return $.Deferred().reject([jqXHR, textStatus, errorThrown]);
    });

    return p;
};
like image 141
jfriend00 Avatar answered Sep 28 '22 02:09

jfriend00