I need to write a setInterval function in javascript. Thi is the code:
var myTimer=setInterval(function(){
var time=0;
$.ajax({
url:'...'
type: "POST",
dataType:"",
success: function (response) {
if(response=="true" || time>=10000){
clearInterval(myTimer);
}
time=time+1000;
},
error: function () {
alert("FAIL");
}
});
},1000);
I don't know why It doesn't stop in clearInterval. Anyone can help me?
You've claimed that the code does "come in the 'if'", so I assume the clearInterval call is actually being made.
Given that, the most likely explanation is that the interval is being cleared (after all, select isn't broken), but before the first "true" response, you've already made more than one ajax call, and the other ones you're seeing are ones scheduled before the interval was cleared.
E.g., your code runs and:
Fires off ajax call #1, which takes more than a second to complete
Fires off ajax call #2
Ajax call #1 completes but isn't "true"
Fires off ajax call #3
Ajax call #2 completes and is "true", clearing the interval
Ajax call #3 completes
Mixing two separate asynchronous intervals (one via setInterval and one via ajax) is asking for trouble.
If the goal is to make the request once a second and stop when you get back "true", I would have the success handler schedule the next call, e.g.:
(function() {
var time = 0;
var started = 0;
start();
function start() {
started = Date.now();
$.ajax({
url: '...'
type: "POST",
dataType: "",
success: function(response) {
if (response != "true") {
// Schedule the next call to occur one second after we
// started the previous call; or almost immediately if
// the call took more than a second to complete
setTimeout(start, Math.max(0, 1000 - (Date.now() - started)));
}
time = time + 1000;
},
error: function() {
alert("FAIL");
}
});
}
})();
Let me illustrate the expected and the actual scenarios to make things clearer.
Scenario #1
The image below shows the case where all your ajax requests complete before one second. You will notice that ajax callback success (or error) functions will execute only before clearInterval (which is what you always expect).
Scenario #2
When some of your ajax requests take more than one second (which is probably what happens), then your ajax callbacks can fire before / after / before-and-after the clearInterval, which makes you feel that your setInterval doesn't stop.
Note that your time variable is useless because it's a function-scoped variable that you initialize to 0 every function call. And even if it's a global variable, it'll only clear the interval in the 11th success function callback, and nothing guarantees how long these 11 successful requests will take.
Solution
As T.J. Crowder suggested, it's better to schedule the next ajax call in the success callback of the previous one, which guarantees that your ajax requests fire sequentially (only one at a time).
Note: Because you edited your question after his answer, then you'll also need to edit the if condition like this:
success: function(response) {
if (response != "true" && time < 10000) {
setTimeout(start, Math.max(0, 1000 - (Date.now() - started)));
}
}
Related
I have about 100 ajax requests that I fire at the same time, I thought browsers only allowed a few requests simultaneously, so the rest would be added to a queue.
The problem however is that jquery/javascript seems to use the timeout value from the time the requests were created via jquery, not from the time the requests were actually executed by the browser. So I get a bunch of timeouts. Is it possible to have the timeout start counting from the time the request is actually going to the URI location, instead of the time it is added by jquery?
You may use the timeout settings for ajax request. You may find the jQuery documentation for the same at : http://api.jquery.com/jQuery.ajax/
However the timeout period starts at the point the $.ajax call is made; if several other requests are in progress and the browser has no connections available, it is possible for a request to time out before it can be sent. Therefore you should set some very large value for the timeout if you wish to follow this approach.
A better approach would be to have a local proxy which entertains all the AJAX calls and fires them in a group of 5-10 and then when all these have finished successfully then it fires the next 5-10 requests.
Here is a kind of queue system. Starts by calling the ajax function N times, and then after each success, calls ajax function again. There is also a check in the success callback to see if all the assets have been loaded...
demo fiddle
$(document).ready(function(e) {
$("form[ajax=true]").submit(function(e) {
e.preventDefault();
var form_url = $(this).attr("action");
var form_method = $(this).attr("method").toUpperCase();
$("#loadingimg").show();
var started = 1, done = 0;
function ajax(){
$.ajax({
url: form_url,
type: form_method,
data: "html=started "+(started++),
cache: false,
success: function(returnhtml){
done++;
$("#result").html(returnhtml);
$("#loadingimg").hide();
if(started <= 100){
ajax();
} else if (done == 100) {
alert("all done!");
}
}
});
}
// how many concurrent calls?
for(i=0;i<10;i++){
ajax();
}
});
});
Checking for internet connection with the following code...
var checkstatus = 0
function checkConnection()
{
$.ajax({
url: "https://path/to/a/file.html",
data: { method: 'checkConnection'},
dataType: "html",
success: function(html)
{
clearInterval(checkstatus);
console.log('connection available');
return;
}
},
error: function(XMLHttpRequest, textStatus, errorThrown){
console.log('retrying');
checkstatus = setInterval(function(){checkConnection()},10000);
}
});
}
The line clearInterval(checkstatus); does not work as the script continues checking every 10 seconds, why is this?
HTTP request is made
Error response comes back
Interval 1 is created calling checkConnection every 10 seconds
HTTP request is made
Error response comes back
Interval 2 is created calling checkConnection every 10 seconds
HTTP request is made
Success response comes back
Interval 2 is cancelled
… but interval 1 is still running.
Don't start the interval inside the error response. Either do it as the means by which you start the very first run of checkConnection or use setTimeout instead.
setTimeout is probably better as it will avoid race conditions in which the request takes more than 10 seconds to get a response.
setInterval sets a callback to run every X seconds, and returns the ID of that callback. You're setting an additional callback to run every time your error callback happens, overwriting the previous interval's ID, but not cancelling it. When you finally do cancel an internval, it's only the last one you created.
Your error callback needs to test that setInterval hasn't already been called:
if (checkstatus == 0)
checkstatus = setInterval(function(){checkConnection()},10000);
I think it is easier if you would use setTimeout instead of the setInterval and you don't need to use the clear it then.
Edits: http://jsfiddle.net/vol7ron/wQZdM/
The fiddle should be used to help illustrate what I'm trying to do and what's happening. The sub-selects should be populated with the second option value.
Original Question:
Not sure the best way to ask. I'm creating a testing script to autofill inputs on a form.
It includes a series of drop-down select boxes, which populate other select options in an onChange event. When trying to auto-populate the form, the sub-selects don't have any options.
console.clear();
// non-select inputs
$(':input:not([type="hidden"],[type="button"])').each(function(){
$(this).val($(this).attr('name')) // test value is simple input's name
});
// select inputs
var count=0, cutoff=7500;
$('select').each(function(){
var t = $(this);
var c = t.children('option');
while( c.length <= 1 && count < cutoff){
count++;
c = $(this).children('option'); // tried not using the cache'd variable
if (!(count % 10))
console.log(count, c.length, "No Options"); // debugging -- never exists early
setTimeout(function(){},0); // not really doing anything
}
t.val( c.eq(1).val() ); // set value to second option value
t.trigger('change'); // calls the onChange even if it doesnt exist
});
// verify it does have data
console.log($('#sub-select').children('option').length); // does have options
There's an AJAX call in the change event. I could modify the callback, but this is just a simple set script for testing, that is run from console. Any ideas?
Not really sure what your code is trying to do
But answering the question How to continue with function after another function call finishes :-
assuming you have a list of functions which are all asynchronous you can nest them to continue
to the next asynchronous function ...
asyncCall(callback1) {
callback1(callback2) {
callback2(...)
}
}
Checkout https://github.com/caolan/async for some elegant ways to do this
this example calls all functions in order even though they are asynchronous.
async.series([
function(callback){
setTimeout(function(){
call_order.push(1);
callback(null, 1);
}, 25);
},
function(callback){
setTimeout(function(){
call_order.push(2);
callback(null, 2);
}, 50);
},
function(callback){
setTimeout(function(){
call_order.push(3);
callback(null, 3,3);
}, 15);
}
1) Use Synchronous AJAX request http://api.jquery.com/jQuery.ajax/
var html = $.ajax({
url: "some.php",
async: false
}).responseText;
2) Instead of using the .each use .eq(index) and just call it in order.
function FakeEach(idx) {
if(idx >= 7500) return;
var $obj = $('select').eq(idx);
if($obj.length == 0) return;
...
$obj.trigger('change');
window.setTimeout(function() { FakeEach(idx++); }, 0);
}
Your problem is that you are starting an AJAX request in the function, and expect that the response arrives before the function ends. As long as you are using an asynchronous request, this will never happen. You have to exit your function before the code that handles the response can run. Javascript is single threaded, so as long as your function is running, no other code can run.
The solution to the problem is to put the code that uses the data in the success callback function that is called after the response has arrived. Eventhough you usually write that function inside the function that makes the AJAX call, it's a separate function that will run later.
If you really need the data in the same function as the AJAX call, you would need to make a synchronous request. This is a killer for the user experience, though, as the entire browser freezes while it is waiting for the response.
Okay, so I appreciate that Javascript is not C# or PHP, but I keep coming back to an issue in Javascript - not with JS itself but my use of it.
I have a function:
function updateStatuses(){
showLoader() //show the 'loader.gif' in the UI
updateStatus('cron1'); //performs an ajax request to get the status of something
updateStatus('cron2');
updateStatus('cron3');
updateStatus('cronEmail');
updateStatus('cronHourly');
updateStatus('cronDaily');
hideLoader(); //hide the 'loader.gif' in the UI
}
Thing is, owing to Javascript's burning desire to jump ahead in the code, the loader never appears because the 'hideLoader' function runs straight after.
How can I fix this? Or in other words, how can I make a javascript function execute in the order I write it on the page...
The problem occurs because AJAX is in its nature asynchronus. This means that the updateStatus() calls are indeed executed in order but returns immediatly and the JS interpreter reaches hideLoader() before any data is retreived from the AJAX requests.
You should perform the hideLoader() on an event where the AJAX calls are finished.
You need to think of JavaScript as event based rather than procedural if you're doing AJAX programming. You have to wait until the first call completes before executing the second. The way to do that is to bind the second call to a callback that fires when the first is finished. Without knowing more about the inner workings of your AJAX library (hopefully you're using a library) I can't tell you how to do this, but it will probably look something like this:
showLoader();
updateStatus('cron1', function() {
updateStatus('cron2', function() {
updateStatus('cron3', function() {
updateStatus('cronEmail', function() {
updateStatus('cronHourly', function() {
updateStatus('cronDaily', funciton() { hideLoader(); })
})
})
})
})
})
});
The idea is, updateStatus takes its normal argument, plus a callback function to execute when it's finished. It's a reasonably common pattern to pass a function to run onComplete into a function which provides such a hook.
Update
If you're using jQuery, you can read up on $.ajax() here: http://api.jquery.com/jQuery.ajax/
Your code probably looks something like this:
function updateStatus(arg) {
// processing
$.ajax({
data : /* something */,
url : /* something */
});
// processing
}
You can modify your functions to take a callback as their second parameter with something like this:
function updateStatus(arg, onComplete) {
$.ajax({
data : /* something */,
url : /* something */,
complete : onComplete // called when AJAX transaction finishes
});
}
I thinks all you need to do is have this in your code:
async: false,
So your Ajax call would look like this:
jQuery.ajax({
type: "GET",
url: "something.html for example",
dataType: "html",
async: false,
context: document.body,
success: function(response){
//do stuff here
},
error: function() {
alert("Sorry, The requested property could not be found.");
}
});
Obviously some of this need to change for XML, JSON etc but the async: false, is the main point here which tell the JS engine to wait until the success call have returned (or failed depending) and then carry on.
Remember there is a downside to this, and thats that the entire page becomes unresponsive until the ajax returns!!! usually within milliseconds which is not a big deals but COULD take longer.
Hope this is the right answer and it helps you :)
We have something similar in one of our projects, and we solved it by using a counter. If you increase the counter for each call to updateStatus and decrease it in the AJAX request's response function (depends on the AJAX JavaScript library you're using.)
Once the counter reaches zero, all AJAX requests are completed and you can call hideLoader().
Here's a sample:
var loadCounter = 0;
function updateStatuses(){
updateStatus('cron1'); //performs an ajax request to get the status of something
updateStatus('cron2');
updateStatus('cron3');
updateStatus('cronEmail');
updateStatus('cronHourly');
updateStatus('cronDaily');
}
function updateStatus(what) {
loadCounter++;
//perform your AJAX call and set the response method to updateStatusCompleted()
}
function updateStatusCompleted() {
loadCounter--;
if (loadCounter <= 0)
hideLoader(); //hide the 'loader.gif' in the UI
}
This has nothing to do with the execution order of the code.
The reason that the loader image never shows, is that the UI doesn't update while your function is running. If you do changes in the UI, they don't appear until you exit the function and return control to the browser.
You can use a timeout after setting the image, giving the browser a chance to update the UI before starting rest of the code:
function updateStatuses(){
showLoader() //show the 'loader.gif' in the UI
// start a timeout that will start the rest of the code after the UI updates
window.setTimeout(function(){
updateStatus('cron1'); //performs an ajax request to get the status of something
updateStatus('cron2');
updateStatus('cron3');
updateStatus('cronEmail');
updateStatus('cronHourly');
updateStatus('cronDaily');
hideLoader(); //hide the 'loader.gif' in the UI
},0);
}
There is another factor that also can make your code appear to execute out of order. If your AJAX requests are asynchronous, the function won't wait for the responses. The function that takes care of the response will run when the browser receives the response. If you want to hide the loader image after the response has been received, you would have to do that when the last response handler function runs. As the responses doesn't have to arrive in the order that you sent the requests, you would need to count how many responses you got to know when the last one comes.
As others have pointed out, you don't want to do a synchronous operation. Embrace Async, that's what the A in AJAX stands for.
I would just like to mention an excellent analogy on sync v/s async. You can read the entire post on the GWT forum, I am just including the relevant analogies.
Imagine if you will ...
You are sitting on the couch watching
TV, and knowing that you are out of
beer, you ask your spouse to please
run down to the liquor store and
fetch you some. As soon as you see
your spouse walk out the front door,
you get up off the couch and trundle
into the kitchen and open the
fridge. To your surprise, there is no
beer!
Well of course there is no beer, your
spouse is still on the trip to the
liquor store. You've gotta wait until
[s]he returns before you can expect
to have a beer.
But, you say you want it synchronous? Imagine again ...
... spouse walks out the door ... now,
the entire world around you stops, you
don't get to breath, answer the
door, or finish watching your show
while [s]he runs across town to
fetch your beer. You just get to sit
there not moving a muscle, and
turning blue until you lose
consciousness ... waking up some
indefinite time later surrounded by
EMTs and a spouse saying oh, hey, I
got your beer.
That's exactly what happens when you insist on doing a synchronous server call.
Install Firebug, then add a line like this to each of showLoader, updateStatus and hideLoader:
Console.log("event logged");
You'll see listed in the console window the calls to your function, and they will be in order. The question, is what does your "updateStatus" method do?
Presumably it starts a background task, then returns, so you will reach the call to hideLoader before any of the background tasks finish. Your Ajax library probably has an "OnComplete" or "OnFinished" callback - call the following updateStatus from there.
move the updateStatus calls to another function. make a call setTimeout with the new function as a target.
if your ajax requests are asynchronous, you should have something to track which ones have completed. each callback method can set a "completed" flag somewhere for itself, and check to see if it's the last one to do so. if it is, then have it call hideLoader.
One of the best solutions for handling all async requests is the 'Promise'.
The Promise object represents the eventual completion (or failure) of an asynchronous operation.
Example:
let myFirstPromise = new Promise((resolve, reject) => {
// We call resolve(...) when what we were doing asynchronously was successful, and reject(...) when it failed.
// In this example, we use setTimeout(...) to simulate async code.
// In reality, you will probably be using something like XHR or an HTML5 API.
setTimeout(function(){
resolve("Success!"); // Yay! Everything went well!
}, 250);
});
myFirstPromise.then((successMessage) => {
// successMessage is whatever we passed in the resolve(...) function above.
// It doesn't have to be a string, but if it is only a succeed message, it probably will be.
console.log("Yay! " + successMessage);
});
Promise
If you have 3 async functions and expect to run in order, do as follows:
let FirstPromise = new Promise((resolve, reject) => {
FirstPromise.resolve("First!");
});
let SecondPromise = new Promise((resolve, reject) => {
});
let ThirdPromise = new Promise((resolve, reject) => {
});
FirstPromise.then((successMessage) => {
jQuery.ajax({
type: "type",
url: "url",
success: function(response){
console.log("First! ");
SecondPromise.resolve("Second!");
},
error: function() {
//handle your error
}
});
});
SecondPromise.then((successMessage) => {
jQuery.ajax({
type: "type",
url: "url",
success: function(response){
console.log("Second! ");
ThirdPromise.resolve("Third!");
},
error: function() {
//handle your error
}
});
});
ThirdPromise.then((successMessage) => {
jQuery.ajax({
type: "type",
url: "url",
success: function(response){
console.log("Third! ");
},
error: function() {
//handle your error
}
});
});
With this approach, you can handle all async operation as you wish.
I've set up an AJAX page refresh with setInterval.
From time to time, the server is so slow that a new request is initiated before the previous one has completed.
How can I prevent that?
Use a timeout value that is shorter than your refresh interval. When the request times out, it will call the error handler so you'll need to differentiate between time out errors and other types of errors in the handler.
$.ajax({
type: "POST",
url: "some.php",
data: "name=John&location=Boston",
timeout: 5000, /* ms or 5s */
success: function(msg){
alert( "Data Saved: " + msg );
}
});
Docs at jquery.com. Example above from same source, but with added timeout value.
Use setTimeout instead, initiate another setTimeout only after receiving the result of the AJAX request. That way a refresh only happens after the specified period since the last refresh.
Instead of using a fixed, hard coded interval: Trigger the next refresh as the last step of handling the current one, e.g. in the "Success" (or "Complete") event callbacks.
You could add a variable that keeps track of the time the current request was sent, so that you can calculate a dynamic delay:
take current time T1
send asynchronous request
other stuff happens...
asynchronous request returns, callback executes
subtract T1 from current time
if result < your desired request interval, set delay value > 0
if result >= your desired request interval, set delay value = 0
call setTimeout with the delay value, initiating the next cycle
What I can tell you is, use a flag in your code.
Like (not what I actually recommend just a simple example)
var isWorking = false;
function doRequest(){
if(isWorking) return;
isWorking = true;
$.ajax({
...,
success: workWithResponse
});
}
function workWithResponse(){
/* doAnythingelse */
isWorking = false;
}
setInterval(doRequest,1000);
Something like that, its primitive but you will avoid race conditions.
Regards.