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;
};
Related
I have a WebApp with a lot of ajax calls, so I did the following to clean up things:
I defined an ajax call with default values:
$.ajaxCall = function (Params){
return $.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: Params.data,
url: Params.url,
});
};
I defined a global ajax errors handler:
$(document).ajaxError(function( event, xhr, settings, error ) {
if(xhr.hasOwnProperty("responseJSON")
&& xhr.responseJSON.hasOwnProperty("Message")) {
let data = settings.hasOwnProperty("data") ? JSON.parse(settings.data) : null;
switch (xhr.responseJSON.Message) {
// Handle errors
}
}
});
And now I would like somewhere to chain the promise returned by $.ajaxCall (that is the promise object of the deferred object returned by $.ajax):
$.ajaxCall({
url: "WebServices/Workspace_Ajax.asmx/WebMethod",
data: JSON.stringify(DataObject)
})
.then(successAjax, function() {
console.log("Rejected, ajax call failed!");
});
But the reject handler of then is not called, while the $.ajaxError function is well called. I don't understand why the Promise is not rejected (for the case where ajax request works, the Promise is well resolved). What am I missing ?
I have two AJAX calls, the second one depends on the first one, so I decided to use promises. I keep getting Uncaught (in promise) abc123 error in console. Note that abc123 is the id I am expecting from the first AJAX call. The second AJAX call never goes inside success function.
var promise = new Promise(function(reject, resolve) {
$.ajax({
url: url1,
dataType: 'json',
success: function (obj1) {
console.log("Got obj1");
resolve(obj1.id);
}
});
});
promise.then((id)=> {
$.ajax({
url: url2,
dataType: 'json',
success: function (obj2) {
console.log("Got obj2");
}
});
});
The structure looks exactly the same as Basic Example. I am really confused about what I am doing wrong.
You mixed up the order of the callback functions: resolve is the first argument, reject is the second, so you when you called the second one (resolve(obj.id)) you actually rejected your promise.
However, notice that you should not use the Promise constructor anyway when dodging jQuery promises - just use Promise.resolve to cast the jQuery promise that $.ajax returns to a native one:
var promise = Promise.resolve($.ajax({
url: url1,
dataType: 'json'
})).then(obj1 => {
console.log("Got obj1");
return obj1.id;
});
promise.then(id => {
return $.ajax({
//^^^^^^ don't forget this
url: url2,
dataType: 'json'
});
}).then(obj2 => {
console.log("Got obj2");
});
I'm making multiple REST calls via Javascript to determine if a user is in several Sharepoint groups.
If the user is not in the group, the REST request returns a status of 500, along with a message saying "user cannot be found".
When an error is returned, I resolve my promise with "false", so my function works ok.
But - every REST response of 500 puts an error entry in the Javascript console - is it possible to suppress those entries?
I know they don't impact the function, but it clutters up the console.
function IsUserInGroupNumber(permissionRequested,userEmail,groupNumber){
var deferred=$.Deferred();
var url=L_Menu_BaseUrl+"/_api/web/sitegroups("+groupNumber+")/Users/getByEmail('"+userEmail+"')/Email";
$.ajax({
type: 'GET',
url: url,
beforeSend: function (XMLHttpRequest) {
XMLHttpRequest.setRequestHeader("Accept", "application/json; odata=verbose");
},
processData: false,
success: function (data)
{
deferred.resolve({requestedPermission:permissionRequested,groupNumber:groupNumber,hasPermission:true});
},
error: function(data){
//user not found in the group returns a 500 error - but return value of 'false'
deferred.resolve({requestedPermission:permissionRequested,groupNumber:groupNumber,hasPermission:false});
}
});
return deferred.promise();
}
The service shouldn't respond with a 500 status code. That means something on the server is failing. You can't control how the JavaScript console / browser interprets and resolves an error or status.
Also, as of jQuery 1.5, $.ajax already returns a promise: http://api.jquery.com/jquery.ajax/
You could simplify your code a great deal with something like:
function IsUserInGroupNumber(permissionRequested, userEmail, groupNumber){
return new Promise(resolve, reject) {
$.ajax({
type: 'GET',
url: L_Menu_BaseUrl + "/_api/web/sitegroups(" + groupNumber + ")/Users/getByEmail('" + email + "')/Email",
beforeSend: function (xhr) {
xhr.setRequestHeader("Accept", "application/json; odata=verbose");
},
processData: false,
})
.done(function() {
resolve(true)
})
.fail(function() {
resolve(false)
})
}
I have an Ember promise call as below;
var promise = new Ember.RSVP.Promise(function(resolve, reject) {
return $.ajax({
//want this common
url: requestUrl,
type: type, // HTTP method
dataType: dataType, // type of data expected from the API response
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(postData)
})
.done(function(data, status, xhrObject) {
//But want this to be different
// resolve call
})
.fail(function(xhrObject, status, error){
// reject call
});
})
My question is can I use common code for $.ajax(), but have different implementation for done() callback
I can check that by passing some parameter from the calling place.
so basically, I want
if (someparam == 'handleDone1')
call resolve(data)
else
call resolve({data})
You are currently passing a function to done by hard coding a function expression into it.
Replace that with a variable. Pass a value to that variable as a function argument.
Alternatively, don't use done here at all. Just return the return value of $.ajax() and call done() on that in the calling function.
return a promise instead of ajax call. And wrap the ajax call into promise.
checkout the below code. It may helps.
function someFunction(resolve1, reject1) {
return new Ember.RSVP.Promise(function(resolve, reject) {
$.ajax({
//want this common
url: requestUrl,
type: type, // HTTP method
dataType: dataType, // type of data expected from the API response
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(postData)
})
.done(function(data, status, xhrObject) {
//But want this to be different
// resolve call
var dicision = resolve1();
if(dicision){
resolve(data);
} else {
resolve({data});
}
})
.fail(function(xhrObject, status, error){
// reject call
});
}
In jquery that is.
I would like something that works as the success-pararameter, but that is run when the function is called, rather than once I get the response.
sample (oajax is an extension of ajax for open auth)
$.oajax({
url: url,
jso_provider: "facebook", // Will match the config identifier
jso_scopes: false, // List of scopes (OPTIONAL)
dataType: 'json',
success: function(data) {
fbposts=data.data
//a bunch of code irellevant for the question
},//success done
error: function() {
console.log("ERROR Custom callback()");
}
})
};
Are you looking for .ajaxSend() ?
Attach a function to be executed before an Ajax request is sent.
This function (and .ajaxComplete et al) allow you to register callback functions that are called for the different phases of every AJAX request.
In a normal ajax function, you pass it as beforeSend:
$.ajax({
url: url,
dataType: 'json',
beforeSend: function(jqXHR, status){
// CODE HERE
},
success: function(data) {
fbposts=data.data
},
error: function() {
console.log("ERROR Custom callback()");
}
})
};
You'll have to check if oajax have this event too, but it probably do