So, the cleanest way to use jQuery ajax in the API as of 2016 is to return a promise. But you cannot determine if the caller has connected an error handler or not.
So, I would suggest that you simply add a new argument to your function, which tells the function NOT to use the default error handling, because the caller will take care of the error handling. And I would advise you to avoid the anti-template promise by simply using the existing $.ajax() promise already returning, and not creating its 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 simply decides whether to apply their own error handling or not:
request(...).then(function(data) {
Or you can go with a callback without promising a failHandler that you will pass, and your default error handling looks like to see if that failHandler passed or not. This is a hybrid of promises and callbacks and is not what I usually choose for the architect, but since your question asks for something that promises does not support, this is one of the achievements:
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; };