Every 3 seconds I make an AJAX POST request to get the status of a process. This works great.
When the process reaches 100% a call back function executes(indicated below) to add new elements to the page and then it cancels the setTimeout method that use to continuously get the progress every 3 seconds. However, I have been told by my users it sometimes fails to cancel and the new elements are not added to the page and I've been that it get stuck at showing "100%".
I have tested this again and again and it never gets stuck for me. The code also looks ok, but my JavaScript skills are not great so I was hoping someone could point out if there is potential of this problem happening?
I have commented the code, apologies its very long. I have tried to reduce it.
function convertNow(validURL){
startTime = setTimeout('getStatus();', 6000);
//AJAX CALL TO RUN PROCESS
$.ajax({
type: "GET",
url: "main.php",
data: 'url=' + validURL + '&filename=' + fileNameTxt,
success: function(msg){
//ON SUCCESS CLEAR SETTIMEOUT AND SHOW ELEMENTS (text)
clearTimeout(continueTime);
clearTimeout(startTime);
$("#loading").hide("slow");
$("#done").html("Done");
}//function
});//ajax
}//function convertNow
function getStatus(){
//AJAX CALL TO GET STATUS OF PROCESS
$.ajax({
type: "POST",
url: "fileReader.php",
data: 'textFile=' + fileNameTxt,
success: function(respomse){
textFileResponse = respomse.split(" ");
$("#done").html("Processing...");
}
});//ajax
clearTimeout(continueTime);
if(textFileResponse[0]=='100.0%'){
clearTimeout(continueTime);
}
else{
clearTimeout(startTime);
continueTime = setTimeout('getStatus();', 3000);
}
}
There's probably a parsing error in the textFileReponse[0]=='100.0%' in some edge cases, with the value in the response not equaling exactly 100.0% (maybe there's extra whitespace, or maybe there are some minor differences on some platforms, etc...). This would cause the code to fall through to the else {} block, and your getStatus function would be queued up again.
EDIT: Given the thread in the comments, it's also an equal likelyhood that there's a race condition going on between the two blocks of Ajax code. (just putting this here for the benefit of readers). END EDIT
What you probably want, in addition to resolving the parsing, however, is to use setInterval(), with only one timer, instead of a startTime and continueTime timer. setTimeout executes only once, whereas setInterval repeats every x milliseconds, so you'd need only one. To cancel a setInterval, use clearInterval.
Related
I'm trying to make a spinner button that will spin while I make an AJAX request and stop when the answer is received.
I've got the AJAX handled but the spinning doesn't seem to work with the following code:
function refresh (id){
var iconElem = document.getElementById("spinner" + id);
iconElem.classList.add('fa-spin');
sleep(5000);
var buttonRefresh = document.getElementById("refreshButton" + id);
buttonRefresh.classList.remove("fa-spin");
};
Note : I have replaced the ajax function with a sleep (implemented elsewhere, but it works like like it should) since I am in a non-php environment.
What happens here is that the the class "fa-spin" is being added while the sleep is over, even though it comes after in the code... Am I missing some kind of "refresh" that I need to execute in order to make the added class effective ?
You need to stop the spinning in the completion callback of the ajax call as it is a async call.
What you are doing here is starting and then immediately stopping the spinner before the ajax call even finishes.
$.ajax({
url: "test.html",
cache: false,
success: function(html){
// stop the spinner here
}
});
Here is the simplest solution with a callback:
function sleep(callback,timeout){
setTimeout(callback,timeout)
}
sleep(() => {
//stop spinner here
},200)
Anyways, I suggest you to read more here
If you are doing an ajax request, you can also use the async:false header to make your request synced, and then your code should work.
Changes to the style or content of the document become effective only when the JavaScript function finishes and returns to the main event loop. Therefore, assuming your sleep() function works as expected (by doing a busy wait or something like that, although that is not actually sleeping), you can only see the total effect of all changes when the function returns. If you follow the advice of the other answers and remove the style in the callback of the AJAX call, you will be fine.
Now the problem below happens only with Safari. Chrome, Opera, IE all work fine.
I have tried:
function do(x){
$.ajax({url: "next.php",
type: "POST",
data: {'cand': x},
success: leave()});
}
function leave(){window.location = "next.php";}
This:
$.post("next.php", {'cand': x});
window.location = "next.php";
And the other two combinations of the above as well. I noticed that when I don't leave the page, the ajax/post request both work, but when I leave the page, they dont. What I mean is when I use the window.location command. As in, the user will leave the page but the post request will not work. Again, this only happens in Safari. I dont't have a Safari browser immediately in front of me so I cannot test it thoroughly.
Can anyone who has any clue let me know what's going on?
If you do this: success: leave() (as in your first example) then you evaluate leave function immediately, and the change of location occurs concurrently to the ajax call. Some browsers will complete the ajax call, some won't.
Instead, do this: success: leave. This will call leave function after the ajax call.
You could try the following, because here the function() is only called after the PHP execution is completed. In your case, it could be that the callback is executed before the Ajax is completed. In that case you could maybe use a little work around and use setTimeout() to wait a short time before leaving.
$.post('next.php', {cand: x}, function() {
// Callback function: called after php script is completed
// setTimeout waits 500ms before leaving
setTimeout(function(){
leave();
}, 500);
})
I am trying to send data from JQuery Ajax to a Generic Handler that calculates something and returns a result. The Ajax request is made inside a for loop at JQuery end. The code looks something like this:
function send(Handler, ids) {
var URL = "http://" + window.location.host + "/Handlers/" + Handler + ".ashx";
var i;
for (i = 0; i < 10; i++) {
var cur = $('.' + ids[i]);
$.ajax({
url: URL,
type: "POST",
data: JSON.stringify({
Data: cur
}),
dataType: "json",
cache: false,
beforeSend: //my before send code,
success: //my success code,
error: //my error code
});
}
alert('Done!');
}
I placed 3 breakpoint in Visual Studio 2012 at line:
$.ajax({
this
alert('Done!');
And third breakpoint at first line in the Generic Handler.
Now, when I try to execute this code, the Ajax works nicely in async way. But, when it reaches the first breakpoint, stops there and then I resume it, instead of reaching the Generic Handler's breakpoint, it continues the loop and goes back to first breakpoint. Then after it reaches the second breakpoint it then stops at generic handler's break point again and again for each value.
So, does that mean that Ajax first collects all the ajax requests and then after the for loop it executes them together?
Javascript is single threaded and non-blocking. This means that in the first iteration won't wait for the ajax call to be completed, it will go back and start the second iteration, and so on.
So, no it doesn't executes them all together. It definately starts the ajax calls in the order of the loop but there is no way to tell what will end first. It might make all the ajax calls and then get an answer (doesn't mean it is the answer of the first iteration), or in the middle of a loop it might be getting answers.
If I am understanding you correctly, you just answered your own question. Ajax is working asynchronously meaning the for loop starts and fires out ajax requests, and continues the loop (the ajax request DOES NOT block)
Therefore it is very likely that the js is performing a loop of code before the request reaches your url (as this has to create a network call)
That said, what are you doing in your beforeSend method? maybe this is making it take enough time that it can perform all iterations of the loop before sending the first request?
To answer your question, no it shouldn't be waiting for the for loop to finish in order to send off the requests, it should be initiating the process as soon as you have made the call
I was following a video tutorial on how to do long polling, and it seems I got it done, but I'm having one issue, for some reason, though I get one response from server, my alert shows up 3-6 times. I thought "success" only happens once if we got one response.
2nd question is, how can I make this javascript code cancel the ajax call every 30 seconds, and restart it? I've put in setInterval with .abort() in there while experimenting with no luck, probably wrong placement.
Thank you for the wisdom and help!
var timestamp = null;
var imp = null;
var annk = null;
function waitForMsg(){
$.ajax(
{
type: "GET",
url: "/test?timestamp=" + timestamp + "&imp=" +imp + "&annk=" +annk,
dataType : 'json',
async: true,
cache: false,
success: function(data)
{
alert("hello");
if(data.annkr > "0"){
$("#myidone").css("background-color", "#cccccc");
}else{
$("#myidone").css("background-color", "#cccccc");
}
if(data.impr > 0){
$("#myidtwo").css("background-color", "#000000");
}else{
$("#myidtwo").css("background-color", "#000000");
}
annk = data.annkr;
imp = data.impr;
timestamp = data.timestamp;
setTimeout('waitForMsg()',2000);
}
});
}
$(document).ready(function(){
waitForMsg();
});
I read stuff on Stackoverflow about readystates, but how do I make sure it's ready only once and does it after it's ready?
If the code is exactly the one you posted, there's no reason to show the alert more than 1 and then after 2s for the rescheduling.
Can you make a jsFiddle for that showing the problem?
The second question is more interesting. You can use the timeout option in the .ajax call and then, in the error handler, just reschedule the call.
My personal suggestion is to refactor your code to use the new JQuery Ajax style base on .done .fail and .always.
And that setTimeout should be written as setTimeout(waitForMsg,2000). Using the string parameter you evaluate that string instead of just calling the function and it's a performance penalty (so small that's hardly noticeable but that is ).
You might be seeing the alert multiple times due to the setTimeout() in your success function, you keep calling the waitForMsg() function. Just a guess.
To abort the request you could do something like this:
var timer = null;
function waitForMessage() {
var req = $.ajax(YOUR_CODE);
// The 30 second timeout
timer = setTimeout(function() {
req.abort();
waitForMessage();
}, 30000);
}
Or slightly better maybe:
function waitForMsg() {
$.ajax({
timeout: 30000,
error: function(err) {
if(err === 'timeout') {
waitForMsg();
}
}
})
}
Actually you should use setInterval instead of setTimeout, using this approach if your ajax call fails, you will forcelly abort the loop.
You could also, cancel or change the timeout timings depending on the usage of your service for network resource sanity.
Is there an event in javascript that I could bind some sort of listener to that will tell me when all javascript/jQuery/Ajax is done executing on the page? The page will not be loading/unloading/reloading, etc between the time the execution begins and the time that I need the listener to "listen", so those events don't work. The page literally is not doing anything. The button is clicked and some javascript functions fire which contain Ajax calls to web services. After all have finished, I want to change window.location. But window.location is changing before the web services have finished in my case.
Currently using setTimeout to achieve this, but as sometimes the code needs more time to run than normal, sometimes the window.location is firing before all the other javascript has finished. Simply put
<input type = "button"... onclick="doThis();";
function doThis() {
try{
//Contains AJAX calls to web services which is mainly what screws up my timing since it may still be trying to execute stuff when the redirect statement happens
}
catch (e) {
}
//Currently doing setTimeout(redirect, 10000);
//Would like to simply detect when all of the above is done and then redirect.
}
Edit: Left out a crucial piece of info. The AJAX calls are in a for loop. The use of variables and success callbacks hasn't been working so well for me as by the time my success callback is executing, my variables have taken on new values in the for loop.
What you are trying to achieve is a classical concurrent programming problem. It is solved by the use of a barrier.
To put it simply, you need to:
Count how many calls you've done.
Set a callback on all AJAX completion events.
Make that callback decrement the number of calls.
The callback checks whether the number of calls has reached zero or not. If yes, then your final code (here, redirect) is called.
The actual implementation is left as an exercise to the reader :)
Hint: embed AJAX calls into a function that handles all counter incrementation and callback setting.
What I do:
Create a variable that represents the number of outstanding AJAX calls.
Before making an AJAX call, increment the variable.
At the end of the code that completes an AJAX call, call a function (e.g. ajaxComplete).
ajaxComplete should decrement the count. When it reaches zero, you know all your calls are complete.
Assuming you're using jQuery.ajax, it sounds like you're looking for ajaxStop.
Why don't you try using something like the Underscore library's after function in the callbacks?
var done = _.after(3, function() {
window.location = 'http://example.com';
});
$.ajax({
url: '/tic',
success: function() {
done();
}
});
$.ajax({
url: '/tac',
success: function() {
done();
}
});
$.ajax({
url: '/toe',
success: function( data ) {
done();
}
});
You should check for the response from AJAX call, and only in that response do redirect. This way you will avoid doing redirect while AJAX was still executing.