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).
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;
};