We have a display board that needs to update every second. The AJAX call to the server triggers a stored proc that is a pretty simple SELECT statement and it takes only milliseconds to execute.
Originally, from time to time because of network latency (or sunspots, or who knows what) this AJAX process would (occasionally, rarely) time out. By tweaking how we handle the timer and by setting the timeout to 0, we have it so it's now running stable and the timeout never happens... yet.
That being said, I'm still nervous that a timeout could still happen. And IF happens, the goal is that it would just keep going. Basically, ignore the timeout and just try again... forever. Nothing like MaxError, or RetryLimit, or TryCount, etc.
Here's what I have now:
setTimeout(function run() {
// When the timer elapses, get the data from the server
GetData();
setTimeout(run, _refreshRate);
}, 1000);
function GetData() {
//console.log("Attempting to obtain the data...");
jQuery.ajax({
url: "something.ashx",
type: "GET",
contentType: 'application/json; charset=utf-8',
success: function(resultData) {
//console.log("Got the data.");
ParseJson(resultData);
// show the last refresh date and time
$('#refreshTime').html(GetDateTime());
},
error : function(xhr, textStatus, errorThrown) {
if (textStatus == 'timeout') {
//console.log("Timeout occured while getting data from the server. Trying again.");
// If a timeout happens, DON'T STOP. Just keep going forever.
$.ajax(this);
return;
}
},
timeout: 0,
});
}
Everything inside of ParseJson(resultData); works great. No issues there. And The timer is setup (I believe) so that it will wait until one GetData() is done before it tries to start another.
I believe that by setting the timeout to 0 that it means "don't ever time out."
My question is this:
Am I correctly handling the error for a timeout? I am using the selected answer in this thread as my guide:
What's the best way to retry an AJAX request on failure using jQuery?
But I don't need a retryLimit limit.
I've also looked at these threads:
How to make the Ajax call again in case Time out error occurs
ajax timeout callback function
I think I've boiled all the info down to a simple solution, but I would like some peer review. Is there a better way to do this?
I'd prefer a solution that only queued a new call when the current had completed. something like..
function poll() {
setTimeout(function () {
GetData();
}, 1000);
}
function GetData() {
jQuery.ajax({
url: "something.ashx",
type: "GET",
contentType: 'application/json; charset=utf-8',
success: function(resultData) {
//...
},
error : function(xhr, textStatus, errorThrown) {
//...
},
complete: function() {
poll();
},
timeout: 0,
});
}
poll();
This way your calls will not risk overlapping anyway.
function GetData() {
//console.log("Attempting to obtain the data...");
jQuery.ajax({
url: "something.ashx",
type: "GET",
contentType: 'application/json; charset=utf-8',
success: function(resultData) {
//console.log("Got the data.");
ParseJson(resultData);
// show the last refresh date and time
$('#refreshTime').html(GetDateTime());
},
error : function(xhr, textStatus, errorThrown) {
if (textStatus == 'timeout') {
//console.log("Timeout occured while getting data from the server. Trying again.");
// If a timeout happens, DON'T STOP. Just keep going forever.
$.ajax(this);
return;
}
},
timeout: 0,
});
}
var myInterval = setInterval(getData, 1000)
// if you want to stop it elsewhere:
// clearInterval(myInterval)
Instead of timeout you could use setInterval
Related
I have a http long polling polling done like this:
xhr = $.ajax({
type: "GET",
url: "http://localhost/pubsub.php",
...
Ajax returns a xhr object. And I use it in another function to abort the long polling request by doing xhr.abort();
After I do this, how can I restart the long polling request? Is there a method to restart?
Thanks
Here is what I am doing in my projects,
If I see a long delay in the ajax response, I simply abort the previous ajax request and make a new request in the same method as shown below,
Note: I don't call the GetData() mmethod in any loop, rather it is a button click action (say refresh) from application user.
function GetData()
{
if (getDataAjaxCall && getDataAjaxCall.readyState != 4)
getDataAjaxCall.abort();
var searchRequest = {};
searchRequest.Criteria = []; //set some criteria
getDataAjaxCall = $.ajax({
url: 'api/DataController/GetData',
type: 'POST',
dataType: 'json',
data: searchRequest,
success: function (data, textStatus, xhr) {
//do tasks you want on success
},
error: function (xhr, textStatus, errorThrown) {
$.unblockUI();
if (textStatus != 'abort') {
//display error message on actual error
}
}
});
}
I hope the above lines of code might give you some idea.
Also, in the error callback, place a check to catch the abort and handle it separately.
There is no API present to restart the AJAX, But you can simulate the restart of AJAX by calling the AJAX again in error callback function.
var xhr;
function callAJAX() {
xhr = $.ajax({
url: "http://stackoverflow.com/dummyURL",
error: function (xhr, e) {
console.log("Error Occured " + e);
callAJAX(); // Restart the AJAX call
}
})
}
callAJAX();
xhr.abort()
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Below is a function that is called when a user clicks the "Save" button on a form. When the PreSaveAction function returns true, the form will be submitted. If false is returned, nothing will happen. I'm using an AJAX call to here to validate form values and would like to have PreSaveAction return true if validateUniqueStaff succeeds, and vise versa if it fails.
function PreSaveAction() {
$.ajax({
url: listUrl,
type: "GET",
dataType: "json",
success: validateUniqueStaff,
error: function (data) {
alert("Error: Problem with the AJAX request");
}
});
//if (validateUniqueStaff succeeds) return true, else return false
}
My trouble is that I can't figure out how to incorporate a deferred object here. I've tried running the ajax call synchronously instead, which works in Chrome, but not in IE8 (a requirement).
I'm absolutely stumped. Any advice would be hugely appreciated! Let me know if I can provide any other information.
It is actually possible!
You need to use the developer tools (F12) to get the current java script code behind the button.
Here's how to proceed next:
//1. unbind the current handler:
var saveButton = ...; //your id (looks like ctl00_***)
var saveButtonCallBack = ...; //call back id (looks like ctl00$...)
//2. When document is ready
jQuery(saveButton).unbind('click').click(function(){
PreSaveActionPeter(
function (){
WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions(saveButtonCallBack, "", true, "", "", false, true))
}
)
});
//3. Define your function:
function PreSaveActionPeter(callBack)
{
jQuery.ajax({
url: *your url*,
method: "GET",
headers: {
"accept": "application/json;odata=verbose",
},
dataType: "json",
success: function (data) {
if (data.d.results.length > 0) {
callBack();
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(XMLHttpRequest.responseText);
}
});
return true;
}
//4. Override of the default PreSaveAction
function PreSaveAction()
{
return false;
}
Your PreSaveAction() is asynchronous. You can't return a success or failure value from that function. It will return long BEFORE the ajax call has completed.
For asynchronous calls (such as Ajax calls), you HAVE to process the result in a callback that gets called sometime later. You can't pretend to use synchronous coding techniques with an asynchronous operation. It just won't work. PreSaveAction() returns long before the ajax call is done so it's return value will not know the end result of the Ajax call.
If your SharePoint environment requires PreSaveAction() to return true/false and you need to do an Ajax operation in order to figure out if you should return true or false, then you're in a bind. You could make the Ajax call be synchronous, but that's generally a horrible user experience because it locks up the browser during the Ajax call and if you're going cross domain, you can't do synchronous anyway.
The best solution would be to understand what your options are with the PreSaveAction() function and see if you can pass a callback into it that will be called with the final result when the asynchronous Ajax call is done.
That would work something like this:
function PreSaveAction(callback) {
$.ajax({
url: listUrl,
type: "GET",
dataType: "json",
success: function(data) {
var retVal = validateUniqueStaff(data);
// call the callback to report the results of validation
callback(retVal);
},
error: function (data) {
alert("Error: Problem with the AJAX request");
callback(false);
}
});
}
The little bit of searching I did on PreSaveAction() in SharePoint indicates that it has to be synchronous. It was designed for client-side validation only (without Ajax calls to the server). To use it, you will have to return true or false directly from it and thus you can't use asynchronous Ajax calls in your PreSaveAction() implementation because the result won't be known in time. You might be able to make your Ajax call synchronous (which I hate because it can be a bad user experience) or you need to find a different way to do your server-assisted validation.
For form validation, it has been standard practise since time immemorial to issue form.submit() from a submit handler on successful validation, and to return false unconditionally.
Presumably, in the SharePoint environment, you can do the same but with the added minor convolution, in this case, that validation be undertaken only after successful receipt of an AJAX response.
Exactly what you write depends on what your validateUniqueStaff returns.
Assuming validateUniqueStaff to return a boolean (valid/invalid), you would write:
function PreSaveAction() {
var form = this;
//disable Save button here
$.ajax({
url: listUrl,
type: "GET",
dataType: "json"
}).then(validateUniqueStaff, function(jqXHR, textStatus, errorThrown) {
return new Error(textStatus);
}).then(function(valid) {
if(valid) {
form.submit();
} else {
return $.Deferred.reject(new Error('Validation failure')).promise();
}
}).fail(function(e) {
alert(e.message);//this will be either the AJAX error message or the validation failure message
}).always(function() {
//re-enable Save button here
});
return false;
}
Or (more elegantly from PreSaveAction's point of view), assuming validateUniqueStaff to return a Promise that is resolved on success or rejected on failure, you would write :
function PreSaveAction() {
var form = this;
//disable Save button here
$.ajax({
url: listUrl,
type: "GET",
dataType: "json"
}).then(validateUniqueStaff, function(jqXHR, textStatus, errorThrown) {
return new Error(textStatus);
}).then(form.submit, function(e) {
alert(e.message);//this will be either the AJAX error message or the validation failure message
}).always(function() {
//re-enable Save button here
});
return false;
}
I really don't think you should be considering synchronous AJAX, which is as unreliable as it is unnecessary.
I previously asked about a question using Ajax polling from a server every 3 seconds using the following jQuery Ajax request:
function getData() {
$.ajax({
url : 'http://example.com',
type: 'GET',
success : function(data) {
// process data here
setTimeout(getData, 3000);
},
dataType : 'json'
});
}
It seems that another way of doing this is putting setTimeout outside $.ajax() block:
function getData() {
setTimeout( function() {
$.ajax({
url : 'http://example.com',
type: 'GET',
success : function(data) {
//process data here
},
dataType : 'json'
}) }, 3000);
}
So is there any difference between these two methods? Do they have the same effect of continuous polling the server every 3 seconds?
Also, inside the success callback function, how do I terminate this infinite polling if certain condition is met, say, data.length>1000 then I want to terminate this loop and call another function? Should I do something like this:
function getData() {
var tID = setTimeout( function() {
$.ajax({
url : 'http://example.com',
type: 'GET',
success : function(data) {
//process data here
if(data.length > 1000) {
funcOutside();
clearTimeout(tID);
}
},
dataType : 'json'
}) }, 3000);
}
The second option won't poll every 3 seconds; it will only poll just once.
To conditionally continue or stop polling you should use a variation of the first option: add a conditional around the setTimeout call.
function getData() {
$.ajax({
url : 'http://example.com',
type: 'GET',
success : function(data) {
// depending on the data, either call setTimeout or simply don't
if( /* data says continue polling */) {
setTimeout(getData, 3000);
}
},
dataType : 'json'
});
}
So is there any difference between these two methods? Do they have the same effect of continuous polling the server every 3 seconds?
Yes, there is an important difference! The first version will queue a call to the function after the response arrives. So the interval between calls will be (roughly) 3000ms plus the time the request/response took.
The second version will make a request after 3 seconds, then stop. If you change setTimeout to setInterval, it would make a new request every 3 seconds, but there would be no guarantee the previous request will already have completed when a new one is made (if one request takes ~3000ms). So the first version is probably what you're looking for.
About terminating the loop: yes, just add a condition like the one you have in your code. But instead of clearing the timeout, just don't add a new one:
//process data here
if(data.length > 1000) {
funcOutside();
} else {
setTimeout(getData, 3000);
}
Final note: technically, that's not recursion, because it's not getData calling itself, but the callback from setTimeout calling getData all the time.
(function loopsiloop(){
setTimeout(function(){
$.ajax({
url: 'foo.htm',
success: function( response ){
// do something with the response
loopsiloop(); // recurse
},
error: function(){
// do some error handling. you
// should probably adjust the timeout
// here.
loopsiloop(); // recurse, if you'd like.
}
});
}, 5000);
})();
This will do the work for you.
I'm doing three things here:
Declaring a function loopsiloop that is immediately invoked (notice the parens at the end).
Declaring a timeout handler to fire after 5 seconds.
Polling the server inside the timeout, which upon either success/failure will call loopsiloop and continue the poll.
I want to make an ajax call after every 1 min but the succeeding call should be made only after the preceding ajax call was completed. For example ajax call 2 should be made only after ajax call 1 is completed.
I know how to make a function execute every 1 min with setInterval.
thanks in advance
Try something like this:
(function repeatAjaxCall() {
$.ajax({
url: "example-url.com",
complete: function() {
setTimeout(repeatAjaxCall, 60000)
}
});
})();
Have you taken a look at the Underscore js debounce function?
http://underscorejs.org/#debounce
Basically, these allow you to call a "debounced" version of the function that will not be called until x number of milliseconds since the last call.
This will allow you to do something like this:
var update = _.debounce(function() {
// do some ajax stuff here
}, 300);
setTimeout(update, 1000);
function ajaxCall(){
setTimeout(function(){
$.get( url, function(data){
// do stuff
ajaxCall();
})
}, 60000)
}
As you are saying. If the next call will be fired just when the previous one has finished. And the previous one can delay more than 1 minutes. Let's supose, 2 minutes. So the next one will be called at least within 2 minutes delay. So knowing that. It's ok that will never work minute by minute, right?
So why not call the next ajax when the last one is completed, instead of fire it minute after minute? Using the complete method:
$.ajax({
url: 'file',
type: 'POST',
data: {},
complete: function(xhr, textStatus) {
//CALL HERE THE NEXT AJAX
},
Or if want to give a time of 1 minute after the the previous one is completed:
$.ajax({
url: 'file',
type: 'POST',
data: {},
complete: function(xhr, textStatus) {
setTimeout(function(){
//CALL HERE THE NEXT AJAX
}, 1000)
},
I'm creating a program that gets data from a server using AJAX/JSON and then used that data to draw onto a canvas (html5). I have a function that when called, does some initialization and then calls this function:
function getClimateData(climateElement) {
$.ajax(
{
type: "GET",
contentType: "application/json; charset=utf-8",
url: "/Climate/yearInformation" + climateElement,
data: "{}",
dataType: "json",
success: function (data) {
weatherData = data;
},
error: function (xhr, err) {
// Note: just for debugging purposes!
alert("readyState: " + xhr.readyState +
"\nstatus: " + xhr.status);
alert("responseText: " + xhr.responseText);
}
}
)
};
The problem is that before all the data has arrived, the calling function continues and draws some stuff on my canvas, based on the data (which isn't there). This only happens sometimes.
How can I make sure the data has arrived before execution continues ?
Do the work…
success: function (data) {
weatherData = data;
// <------------------ HERE!
},
… and not in the calling function.
That is the point of having a callback function.
Move the code that draws based on the data to a separate function call in the success function - or just put the code directly in the success function.
...
success: function (data) {
// use data to do your drawing
},
...
It is theoretically possible to put the request into synchronous mode using async: false but it's usually a bad idea.
The best way to deal with this is to change your script's structure so everything dependent on the request happens in the success callback (or gets called from there).
You should put the drawing function in your success callback. This way, the drawing functionality is only triggered when the data arrives.