Is it OK to call clearInterval inside a setInterval handler? - javascript

I have a piece of Javascript that checks for a condition (via an AJAX call) every n seconds. If that condition is true, it stops checking. I have implemented it in the following way:
var stopTimer;
var timerId = setInterval(function() {
/* Make Ajax Calls and set stopTimer */
if (stopTimer) {
clearInterval(timerId);
}
}, 10000);
However, I find erratic behaviour: Works sometimes, but at other times, it keeps checking forever. I have checked that (as much as is possible) there is no error in any part of the code.
I am therefore suspecting that calling clearInterval inside a setInterval handler might be the culprit. Is that right? Is it OK to call clearInterval inside a setInterval handler?
Thank you for your attention

It's safe. The issue is probably to do with stopTimer not being set as you expect.

I don't think there will be any issue with your code unless the AJAX function is erroneous. You have to take care of the success and error callbacks of the AJAX function so that there won't be any issue with the loop not being stopped.
Also I think you are constantly polling the server for a response and then doing the appropriate action. You can use Reverse AJAX to do this kind of process.

Make sure you're not inadvertently re-using the same timer name elsewhere in your code which would result in you always stopping the second timer to be defined.
Either give the timer a unique name, or scope it to a function
var timerForAjax = setInterval(function() {
/* Make Ajax Calls and set stopTimer */
if (stopTimer)
{
clearInterval(timerForAjax);
}
}, 10000);
I was careless enough to call my timer interval and didn't realize I was creating two timers in the same scope both called interval. Blamed iOS8 for about an hour until I realized that that was nothing to do with it.

Related

jquery setTimeout too much recursion

I have read from multiple places that setTimeout() is preferable to setInterval() when setting something up to basically run forever. The code below works fine but after about an hour of running Firefox (38.0.1) throws an error of too much recursion.
Essentially I have it grabbing a very small amount of text from counts.php and updating a table with that information. The whole call and return takes about 50ms according to the inspectors. I'm trying to have it do this every x seconds as directed by t.
I suspect if I switch to setInterval() this would probably work, but I wasn't sure what the current state of the setTimeout() vs setInterval() mindset is as everything I've been finding is about 3-5 years old.
$(document).ready(function() {
t = 3000;
$.ajaxSetup({cache: false});
function countsTimer(t) {
setTimeout(function () {
$.getJSON("counts.php", function (r) {
$(".count").each(function(i,v) {
if ($(this).html() != r[i]) {
$(this).fadeOut(function () {
$(this)
.css("color", ($(this).html() < r[i]) ? "green" : "red")
.html(r[i])
.fadeIn()
.animate({color: '#585858'}, 10000);
})
};
});
t = $(".selected").html().slice(0,-1) * ($(".selected").html().slice(-1) == "s" ? 1000 : 60000);
countsTimer(t);
});
}, t);
};
countsTimer(t);
});
Update: This issue was resolved by adding the .stop(true, true) before the .fadeOut() animation. This issue only occurred in Firefox as testing in other browsers didn't cause any issues. I have marked the answer as correct in spite of it not being the solution in this particular case but rather it offers a good explanation in a more general sense.
You should indeed switch to setInterval() in this case. The problem with setInterval() is that you either have to keep a reference if you ever want to clear the timeout and in case the operation (possibly) takes longer to perform than the timeout itself the operation could be running twice.
For example if you have a function running every 1s using setInterval, however the function itself takes 2s to complete due to a slow XHR request, that function will be running twice at the same time at some point. This is often undesirable. By using setTimout and calling that at the end of the original function the function never overlaps and the timeout you set is always the time between two function calls.
However, in your case you have a long-running application it seems, because your function runs every 3 seconds, the function call stack will increase by one every three seconds. This cannot be avoided unless you break this recursion loop. For example, you could only do the request when receiving a browser event like click on the document and checking for the time.
(function()
{
var lastCheck = Date.now(), alreadyRunning = false;
document.addEventListener
(
"click",
function()
{
if(!alreadyRunning && Date.now() - lastCheck > 3000)
{
alreadyRunning = true;
/* Do your request here! */
//Code below should run after your request has finished
lastCheck = Date.now();
alreadyRunning = false;
}
}
)
}());
This doesn't have the drawback setInterval does, because you always check if the code is already running, however the check only runs when receiving a browser event. (Which is normally not a problem.) And this method causes a lot more boilerplate.
So if you're sure the XHR request won't take longer than 3s to complete, just use setInterval().
Edit: Answer above is wrong in some aspects
As pointed out in the comments, setTimeout() does indeed not increase the call stack size, since it returns before the function in the timeout is called. Also the function in the question does not contain any specific recursion. I'll keep this answer because part of the question are about setTimeout() vs setInterval(). However, the problem causing the recursion error will probably be in some other piece of code since there is not function calling itself, directly or indirectly, anywhere in the sample code.

How to put a sleep after clicking a button

I would like to put a delay after a button is pressed in order for the button to load the data from the cache before executing the next line of code. Would putting a sleep be the best way to do this?
Something like this or is there an alternative approach to best solve this problem?
setInterval(document.getElementById("generateButton"), 1000);
Don't use setInterval to do this. It doesn't have the functionality you seem to desire (it repeats). Instead, use jQuery and do something like this:
$("#generateButton").click(function(event){
setTimeout(function(){
//Do what the button normally does
}, 1000);
});
Or (without JQuery):
var generateButton = document.getElementById("generateButton");
generateButton.addEventListener("click", function(){
setTimeout(function(){
//Do what the button normally does
}, 1000);
});
Using setTimeout over setInterval is preferred in your case because setTimeout runs only once while setInterval runs multiple times.
I assume you have, in your html, <button id='generateButton' onclick='someFunction()'>Button Text</button>. Remove the onclick='someFunction() and put your someFunction() where I said (in the examples) "Do what the button normally does."
You can also add in the code that loads the cache a method that calls another method once the cache has been loaded (when the someFunction() from the button is called, it loads the cache, and at the end of the function (set this up using callbacks), once the cache has been loaded, it calls another method onCacheLoaded() that can be run once the cache has been loaded.
You should use callbacks, so the moment you loaded data from cache you can call it and continue executing the rest of the script.
You cannot use interval since you cannot be sure how much time is needed for the data to load. Though keep in mind the asynchronous nature of javascript and don't block the part of the script that does not depend on the data that's being loaded.
Try setTimeout:
myButton.addEventListener('click', function() {
setTimeout(delayed, 1e3); // Delay code
}, false);
function delayed() {
// Do whatever
}
Note setInterval runs a function periodically, setTimeout only once.
Also note that the delayed code must be a function (or a string which will be evaluated, but better avoid that). However, document.getElementById("generateButton") returns an html element (or null).

Prevent JavaScript Timer Re-entry

My ASP.NET MVC page uses JavaScript/jQuery to poll my database every second.
This is working but I want to make sure that, if there is a delay, my timer handler won't get called again before it has returned.
In there any trick to this other than storing the timer ID in a global variable, clearing the timer in my handler, and restarting it when my handler is done.
NOTE: I realize every second seems frequent but this code is polling my server after submitting a credit card payment. Normally, it will only run for a second or so, and I don't want the user to wait any longer than necessary.
Polling every second? That's quite heavy!
That aside, you won't have this issue when setTimeout is used instead of setInterval. The latter ensures that a piece of code is run x times given a interval, while the former ensures that there's a delay of at least x milliseconds.
function some_poller() {
$.ajax({
url: '/some_page',
success: function() {
setTimeout(some_poller, 1000);
},
error: function() { // Also retry when the request fails
setTimeout(some_poller, 1000);
}
});
}
// Init somewhere
some_poller();
Not really, although I wouldn't recommend using a global variable. Stick it inside some function.
But are you really sure you need to poll every second? That's an extremely chatty interface.
In my personal experience a "global", (inside of the root function), variable works very well in this instance so that you can control when to clear and restart. If the response is really as quick as you say, this shouldn't cause too much overhead, (clearing/resetting), and will allow to account for these type of situations.

setTimeout, clearTimeout in closure

var clearId:int = setTimeout(function():void{
//some code here
clearTimeout(clearId);
},2000);
Is this valid AS3? Anyone see problems with it?
Whilst this compiles without warnings or errors, and is valid AS3, there is un-needed code.
The setTimeout function only runs a function once.
The setInterval function on the other hand runs a function at a specified interval until clearInterval is called.
clearTimeout is used to stop a timeout event that has been set occuring.
Because the timeout only occurs once, there is no need to clear it in the timeout function.
If you were using setInterval, then you would need to clearInterval when you no longer wanted the function to be called.
Is it valid? Yes.
Does it work? Yes.
Does it make any sense? No.
A timeout only executes once. The closure necessarily is only called after the timeout has already completed. Now, if you were using an interval, it would make sense.
The method works because the compiler defines all of those local variables up front, making them automatically accessible. Basically, it does something like this:
var clearId:int;
clearId = setInterval(function():void
{
clearInterval(clearId);
}, 2000);

jQuery infinite function execution

We want to know if it is possible to have a function using jQuery to inspect a number of elements and, depending on the types assigned to them by one click, perform other functions. Basically, a function that would run forever, while the user does not refresh the page.
The idea is not to depend on events clicks to perform a function, but the classes assigned to a specific element.
For example:
$("td.gantt").each(function() {
if($(this).hasClass("oper")) {
//execute a serie of functions
}
if($(this).hasClass("preop")) {
//execute a serie of functions
}
});
The above is executed once, and we need to run all the time.
// define a function...
function ganttEach() {
$("td.gantt").each(function() {
// ...
});
}
// ...repeat it once every second
window.setInterval(ganttEach, 1000);
You can't "let it run all the time" (like, in a while(true) loop) because JavaScript is single-threaded and blocking the thread means your other code will never run. setInterval() makes sure there are necessary "gaps" for other code to execute.
setInterval() returns an ID that you can store in a variable and feed to clearInterval() at some point to make it stop again.
If you want to make sure that every new iteration of your function starts only after the previous one has really finished, use setTimeout() instead:
// define a self-repeating function...
function ganttEach() {
$("td.gantt").each(function() {
// ...
});
window.setTimeout(ganttEach, 1000); // calls itself again in one second
}
// ...initiate self-repeating function
ganttEach();
You should probably include some way to stop the endless repetition here as well, like introducing a flag that's checked before the setTimeout() call.
You can run your check every few milliseconds, say 50ms, using setInterval
window.setInterval (function () {
// do checks here
}, 50);
You might end up using a lot of CPU power if your checks are too frequent, or too complicated.
It is possible, with setInterval. My advice would be to select the element outside of the repeating function so as to minimize the overhead.
An infinite loop would lock the browser UI, as it is a single threaded environment. Set interval, however let you add actions to the UI stack which will be executed after a given period of time. You can specify this period in the second parameter of setInterval.
// select the element outside
// to minimize overhead
$gantt = $("td.gantt");
// define a repeating action
setInterval(function() {
$gantt.each(function() {
if($(this).hasClass("oper")) {
//execute a serie of functions
}
if($(this).hasClass("preop")) {
//execute a serie of functions
}
});
}, 100); // repeat interval: 100ms
I am not sure exactly what you are trying to do, but have you tried setInterval? It will keep running if that is what you really want.
window.setInterval(function () {
// add your jQuery here
}, 100);

Categories