Detect when any ajax calls fail - javascript

I have a website where users can work on projects and their work gets automatically saved to my database. Every couple seconds on my site an ajax (post) call occurs (usually in jquery) to check permissions and what not.
With one section of code is there any way so check if any of the ajax calls on your page fail. I don't want to have to go to every individual call and add a block of code to the end.
Basically this is so I can alert the user if they have lost connection or if something is going wrong.

You can use the jQuery event ajaxError. It will be triggered whenever an Ajax request completes with an error:
$(document).ajaxError(function() {
console.error('Error');
});
Check out the documentation.

$(document).ready(function(){
//ajax setup settings
$.ajaxSetup ({
cache: false,
async: false,
statusCode: {
404: function() {
alert('Page not found!');
},
500: function(jqXHR, textStatus) {
alert('Server side: ' + textStatus);
}
}
});
});
I hope this may help you

I would suggest you to override the original jquery ajax function.
var $_ajax = $.ajax; // reference to original ajax
$.ajax = function(options) {
if (options.error) {
// reference to original error callback
var originalErrorHandler = options.error;
var errorHandlerContext = options.context ? options.context : $;
var customErrorHandler = function(xhr, status, error) {
// notify error to your user here
};
// override error callback with custom implementation
options.error = customErrorHandler;
};
return $_ajax.apply($, arguments);
}

Related

Javascript- How to check if operation has been completed on this event

Is there any way to check if the event is completed and element is free to perform another action?
Like I want to do
$('#button-cancel').on('click', function() {
// send ajax call
});
/****************************************
extra code
*******************************************/
$('#button-cancel').on('click', function() {
if(ajax call is completed) {
//do some thing
}
});
I don't want to send ajax call in second onclick as it is already been sent, just want to check if it is done with ajax then do this
You can introduce a helper variable:
// introduce variable
var wasAjaxRun = false;
$('#button-cancel').on('click', function() {
// in ajax complete event you change the value of variable:
$.ajax({
url: "yoururl"
// other parameters
}).done(function() {
// your other handling logic
wasAjaxRun = true;
});
});
$('#button-cancel').on('click', function() {
if(wasAjaxRun === true) {
//do some thing
}
});
EDIT: I just noticed that you have event handlers attached to the same button. In that case my initial answer would not work, because first event hander would be executed every time you click the button.
It is not very clear from the description what you want to do with your first event hander. I assume you want to use some data, and if you already have this data, then you use it immediately (like in second handler), if you don't have it - you make the AJAX call to get the data (like in first handler).
For such scenario you could use single event handler with some conditions:
var isAjaxRunning = false; // true only if AJAX call is in progress
var dataYouNeed; // stores the data that you need
$('#button-cancel').on('click', function() {
if(isAjaxRunning){
return; // if AJAX is in progress there is nothing we can do
}
// check if you already have the data, this assumes you data cannot be falsey
if(dataYouNeed){
// You already have the data
// perform the logic you had in your second event handler
}
else { // no data, you need to get it using AJAX
isAjaxRunning = true; // set the flag to prevent multiple AJAX calls
$.ajax({
url: "yoururl"
}).done(function(result) {
dataYouNeed = result;
}).always(function(){
isAjaxRunning = false;
});
}
});
You should be able to provide handlers for AJAX return codes. e.g
$.ajax({
type: "post", url: "/SomeController/SomeAction",
success: function (data, text) {
//...
},
error: function (request, status, error) {
alert(request.responseText);
}
});
you can disable the button as soon as it enters in to the event and enable it back in ajax success or error method
$('#button-cancel').on('click', function() {
// Disable button
if(ajax call is completed) {
//do some thing
//enable it back
}
});
This is edited, more complete version of dotnetums's answer, which looks like will only work once..
// introduce variable
var ajaxIsRunning = false;
$('#button').on('click', function() {
// check state of variable, if running quit.
if(ajaxIsRunning) return al("please wait, ajax is running..");
// Else mark it to true
ajaxIsRunning = true;
// in ajax complete event you change the value of variable:
$.ajax({
url: "yoururl"
}).done(function() {
// Set it back to false so the button can be used again
ajaxIsRunning = false;
});
});
You just need to set a flag that indicates ajax call is underway, then clear it when ajax call returns.
var ajaxProcessing = false;
$('#button-cancel').on('click', function(){
processAjaxCall();
});
function processAjaxCall() {
if(ajaxProcessing) return;
ajaxProcessing = true; //set the flag
$.ajax({
url: 'http://stackoverflow.com/questions/36506931/javascript-how-to-check-if-operation-has-been-completed-on-this-event'
})
.done(function(resp){
//do something
alert('success');
})
.fail(function(){
//handle error
alert('error');
})
.always(function(){
ajaxprocessing = false; //clear the flag
})
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="button-cancel">Cancel</button>
What you can do is call a function at the end of an if statement like
if(ajax call is completed) {
checkDone();
}
function checkDone() {
alert("Done");
}

Creating ajax request loop within an 'each' function

This topic is covered in a few other questions, but I had some difficulty applying the suggested approaches into this use case. I have a checkbox list, where a user can select n sub-sites to publish their post to. since this list could grow to be 100+, I need an efficient way to perform an expensive task on each one. It's okay if it takes awhile, as long as Im providing visual feedback, so I planned to apply an "in progress" style to each checkbox item as its working, then move to the next item int he list once it is successfully published. Also note: I'm working in the WordPress wp_ajax_ hook but the PHP side of things is working well, this is focused on the JS solution.
This code is working right now (console.logs left in for debug), but I've seen multiple warnings against using async: true. How can I achieve a waterfall AJAX loop in a more efficient way?
//Starts when user clicks a button
$("a#as_network_syndicate").click( function(e) {
e.preventDefault(); //stop the button from loading the page
//Get the checklist values that are checked (option value = site_id)
$('.as-network-list').first().find('input[type="checkbox"]').each(function(){
if($(this).is(':checked')){
blog_id = $(this).val();
console.log(blog_id+' started');
$(this).parent().addClass('synd-in-progress'); //add visual feedback of 'in-progress'
var process = as_process_syndication_to_blog(blog_id);
console.log('finished'+blog_id);
$(this).parent().removeClass('synd-in-progress');
}
});
});
function as_process_syndication_to_blog(blog_id){
var data = {
"post_id": $('#as-syndicate_data-attr').attr("data-post_id"), //these values are stored in hidden html elements
"nonce": $('#as-syndicate_data-attr').attr("data-nonce"),
"blog_id": blog_id
};
var result = as_syndicate_to_blog(data);
console.log('end 2nd func');
return true;
}
function as_syndicate_to_blog(data){
$.ajax({
type : "post",
dataType : "json",
async: false,
url : ASpub.ajaxurl, //reference localized script to trigger wp_ajax PHP function
data : {action: "as_syndicate_post", post_id : data.post_id, nonce: data.nonce, blog_id: data.blog_id},
success: function(response) {
if(response.type == "success") {
console.log(response);
return response;
} else {
}
},
error: {
}
});
}
Indeed, doing synchronous AJAX request is bad because it will block the browser during the whole AJAX call. This means that the user cannot interact with your page during this time. In your case, if you're doing like 30 AJAX calls which take say 0.5 seconds, the browser will be blocked during 15 whole seconds, that's a lot.
In any case, you could do something following this pattern:
// some huge list
var allOptions = [];
function doIntensiveWork (option, callback) {
// do what ever you want
// then call 'callback' when work is done
callback();
}
function processNextOption () {
if (allOptions.length === 0)
{
// list is empty, so you're done
return;
}
// get the next item
var option = allOptions.shift();
// process this item, and call "processNextOption" when done
doIntensiveWork(option, processNextOption);
// if "doIntensiveWork" is asynchronous (using AJAX for example)
// the code above might be OK.
// but if "doIntensiveWork" is synchronous,
// you should let the browser breath a bit, like this:
doIntensiveWork(option, function () {
setTimeout(processNextOption, 0);
});
}
processNextOption();
Notice: as said by Karl-André Gagnon, you should avoid doing many AJAX requests using this technique. Try combining them if you can, it will be better and faster.
If you can't pass the whole block to the server to be processed in bulk, you could use a jQuery queue. This is using your sample code as a base:
var $container = $('.as-network-list').first();
$container.find('input[type="checkbox"]:checked').each(function(){
var $input = $(this);
$container.queue('publish', function(next) {
var blog_id = $input.val(),
$parent = $input.parent();
console.log(blog_id+' started');
$parent.addClass('synd-in-progress'); //add visual feedback of 'in-progress'
as_process_syndication_to_blog(blog_id).done(function(response) {
console.log(response);
console.log('finished'+blog_id);
$parent.removeClass('synd-in-progress');
next();
});
});
});
$container.dequeue('publish');
function as_process_syndication_to_blog(blog_id){
var data = {
"post_id": $('#as-syndicate_data-attr').attr("data-post_id"), //these values are stored in hidden html elements
"nonce": $('#as-syndicate_data-attr').attr("data-nonce"),
"blog_id": blog_id
};
return as_syndicate_to_blog(data).done(function(){ console.log('end 2nd func'); });
}
function as_syndicate_to_blog(data){
return $.ajax({
type : "post",
dataType : "json",
url : ASpub.ajaxurl, //reference localized script to trigger wp_ajax PHP function
data : {action: "as_syndicate_post", post_id : data.post_id, nonce: data.nonce, blog_id: data.blog_id}
});
}
I don't have a test environment for this so you may need to tweak it for your use case.

Why is it so hard to ping my own Server?

I got this piece of cake function:
$.ajax({
url: 'Example.html',
DataType: 'text',
cache: false,
success: function (){
alert ('Yes');
},
error: function (){
alert ('No');
}
});
This function, works just fine, BUT ONLY FOR THE VERY FIRST TIME), from the second time on, the function sends the following error to Chrome:
GET http://MyServer.com/Example.html?_=1406469092100 net::ERR_FAILED
The same situation happens equally with this second JS option:
function doesConnectionExist() {
var xhr = new XMLHttpRequest();
var file = "http://www.example.com/Example.html";
var randomNum = Math.round(Math.random() * 10000);
xhr.open('HEAD', file + "?rand=" + randomNum, false);
try {
xhr.send();
if (xhr.status >= 200 && xhr.status < 304) {
alert ('Yes');
} else {
alert ('No');
}
} catch (e) {
alert ('No');
}
}
1) In the Ajax scenario I just indicate cache: "false"!
2) In the JavaScript scenario I am using random arguments to avoid cache!
Is there anything I am missing? in the Server side??
Please help...
It may be a server problem? I have created a jsFiddle and it seems to work like it should. I wrapped your $.ajax method in a ping function and run it 3 times fetching a jsfiddle resource.
function ping(i) {
$.ajax({
url: '/img/logo.png',
success: function () {
screen.log({text: 'attempt #'+(i+1)+ ' Yes', timed: +i, clear: 'no'});
},
error: function (d){
screen.log({text: 'attempt #'+(i+1)+ ' Nope', timed: +i, clear: 'no'});
}
});
}
See the already mentioned jsFiddle for output
Note: in your second code snippet your supply false as the third paramater to the open method, which means asynchronous = false, so the XHR there is synchronous. The cache: false parameter in the first snippet appends a random reuqeststring to the request. This prevents the browser from caching the fetched resource. I suppose it's not really necessary here.
After hours and hours of try an error I found the Answer....dedicated for those guys who are facing the same problem I did:
In my case this was not a common Web Page, it was an "Offline Web Page" with a Manifest File in it.
Simply in the section "NETWORK" of the manifest file included the file "Example.html" and that's it.
That's all folks!

no Callback in JSONP in an iframe with data

I am using chrome.
I have an iframe in which i require to hit a url that supports jsonp.
so i used this code :
$.ajax({
dataType: 'jsonp',
url: my_url.endpoint + '/login/v1/token' ,
data: form_to_object("#signin_form"),
context: window,
// All Ajax calls to ABC are json
// Response statuses other than 200 are caught in a timeout
timeout: 10000, //10s
// Handler for successful calls to ABC: calls that return with statusCode 200
success: function(data, textStatus, jqXHR) {
// console.log(data);
alert("in access_token success");
if (data.hasOwnProperty('error_flag')) {
// Errors associated with this action are caught here:
// invalid_credentials, account_lockout, etc.
if (data.hasOwnProperty("jump")) {
ABC_show_frame(data.jump);
} else {
ABC_error_handler(data);
}
return;
}
// Auth succeeded, we can log in the user
GetUserProfile(data);
// ABC_success_handler(data);
},
error: function(data, textStatus, jqXHR) {
alert("In access_token error");
if (data.hasOwnProperty("jump")) {
ABC_show_frame(data.jump);
} else {
ABC_error_handler(data);
}
}
});
Now this code does not attach a callback=some_random_function_name in the url that it generates after attaching the parameters of data.
like https://abc/login/v1/token?username=ashish?password=abc but no callback.
When i debug it line by line, it do call the url with callback=something, and it seems to work. (seems because may be sometime it does not attach even in debugging line by line.)
But when i just run it, it does not.
I think that may be the problem is a bug in jquery where it also has to attach data that it got from form_to_object() and may be that overrides the callback parameter. But that is just a guess.
What should i do ?
I had a form and i was writing my own custom function that would be called when submit button of the form was clicked. In that function i was not stopping the event from propagating further. This lead to this weird errors.
$("form.ims.ajax").submit(function(event) {
event.preventDefault();
// do your stuff
});
This solved the problem.

JavaScript timing issue - window closing before sessionStorage values are set

We are putting together an FSSO API that requires a popup window for the user to log in. In the popup, we perform two tasks:
Calling a service to populate a profile values, then setting a page
to redirect the user to based on the event type (login or registration).
Redirecting the user to the redirect page in the parent window and closing the FSSO popup.
Code:
$(document).ready(function() {
var nextPage = "index.html",
storage = window.opener.sessionStorage;
function setStorage(callback){
$.ajax({
type: "GET",
cache: false,
dataType: "json",
url: "https://someserviceURL/service/profile",
success: function(objJSON){
//storage.op is set on the parent page when login or reg link is clicked
if (storage.op == "login") {
storage.firstname = objJSON.firstName;
storage.lastname = objJSON.lastName;
storage.setItem('loggedIn',JSON.stringify(true)); //Some browsers don't support booleans in sessionStorage
nextPage = "dashboard.html";
}
else if (storage.op == "register") {
storage.firstname = objJSON.firstName;
storage.lastname = objJSON.lastName;
storage.setItem('loggedIn',JSON.stringify(true));
nextPage = "options.html";
}
},
error: function( jqXHR, textStatus, errorThrown){
//display error message
}
});
callback();
}
setStorage(function(){
if (typeof window.opener != "undefined" && window.opener != null){
setTimeout(function(){
window.opener.location.href = nextPage;
window.close();
},3000);
}
});
});
Problem:
The window seems to be closing before I'm able to set the sessionStorage values if I set the timeout to anything less than 3000. I just want to close the window in response to those values being set, not some arbitrary amount of time passing. I tried the trick of setting the timeout to 0 but no luck, and I tried just the callback with no timeout.
Looking for best practices here on handling timing issues like these, what I have now feels hacky. :)
The call to $.ajax() is asynchronous, which means that the rest of the script will continue to execute immediately after the call is made, without waiting for the call to complete and trigger the success or error handlers.
This means that your function called callback is being executed before your success handler. It may usually work as intended with a 3000ms timeout as your web service often takes less time than this to complete, so your callbacks are executed first in these cases. As you mentioned, this is not a reliable way to control the order of events.
One solution would be to execute your callback as part of the complete handler, as follows:
$(document).ready(function() {
var nextPage = "index.html",
storage = window.opener.sessionStorage;
function setStorage(callback){
$.ajax({
type: "GET",
cache: false,
dataType: "json",
url: "https://someserviceURL/service/profile",
success: function(objJSON){
//storage.op is set on the parent page when login or reg link is clicked
if (storage.op == "login") {
storage.firstname = objJSON.firstName;
storage.lastname = objJSON.lastName;
storage.setItem('loggedIn',JSON.stringify(true)); //Some browsers don't support booleans in sessionStorage
nextPage = "dashboard.html";
}
else if (storage.op == "register") {
storage.firstname = objJSON.firstName;
storage.lastname = objJSON.lastName;
storage.setItem('loggedIn',JSON.stringify(true));
nextPage = "options.html";
}
},
error: function( jqXHR, textStatus, errorThrown){
//display error message
},
complete: function( jqXHR, textStatus){
callback();
}
});
}
setStorage(function(){
if (typeof window.opener != "undefined" && window.opener != null){
window.opener.location.href = nextPage;
window.close();
}
});
});
Or if you don't care about the returned arguments you could pass callback directly to complete. Note that complete will be executed in both success and error conditions, so you might want to call callback only in your success handler and do something else with any errors.
You should call your callback as the last action of your success function:
.
.
.
else if (storage.op == "register") {
storage.firstname = objJSON.firstName;
storage.lastname = objJSON.lastName;
storage.setItem('loggedIn',JSON.stringify(true));
nextPage = "options.html";
}
callback(); // execute your callback to the window closer here.
},
error: function( jqXHR, textStatus, errorThrown){
//display error message
}
});
}

Categories