JS setTimeout in while loop - javascript

Since setTimeout crashes in while loops.
I don't know if there is a way to do it but I am trying to make one.
This is how it looks so far.
<script>
var send = true;
function sendit()
{
alert("test");
return true;
}
while(true)
{
if(send == true)
{
send = false;
setTimeout(function(){
if(sendit() == true) {
send = true;
}
}, 5000);
}
}
</script>
Is it possible this way?

You haven't explained what you want your code to do. If you want it to alert "test" every 5 seconds then you need this:
<script>
function sendit()
{
alert("test");
// Call sendit() the next time, repeating
setTimeout(sendit, 5000);
}
// Call sendit() the first time
setTimeout(sendit, 5000);
</script>
No need for a loop, just get the function to schedule itself again.

My understanding is that what you're trying to do is the equivalent of Thread.sleep(5000) in a language like Java or C#. That functionality does not exist in JavaScript. If you want to do something some amount of time after your function's execution, put it in a timeout, but one way or another, that first function will still complete in the same frame unless you're performing an enormous amount of work.
Currently, your code is setting a timeout on sendit() a practically-infinite number of times before it returns. Since JavaScript is single threaded, even if 20 seconds passed, it still wouldn't have finished your function and couldn't start looking up timeouts it needs to process. What you should be doing is something like having the inside of the timeout set another timeout, and remove the enclosing while(true). That could allow for infinite, periodic behavior as I think you're looking for.

Related

How to stop a function during its execution - JavaScript

How can I stop/terminate a function which is already executed and still running? For example, I have this function:
function foo() {
setInterval(function() {
console.log("Foo Executed !");
}, 3000);
}
foo();
Now, this foo() function will run for unlimited time, when a specific event occurs, let's say a stop button has been clicked, then I want to stop this function.
In my case the function doesn't have setInterval() function. And what if the foo() function don't have the setInterval() method, but just many lines of code that get executed and I want to stop it from executing after a specific event.
Stopping a running function is a bit different than what you are actually showing for code, which is an asynchronous operation happening outside of the function that initiated it.
Running functions can only be terminated from within the function and that is done with either a return statement or by throwing an exception.
return can be called conditionally so that the function doesn't always exit at the same point. This is often the case with form validation functions - - if something is determined to be invalid, a return is encountered so that the form is not submitted. If everything is valid, the return is skipped and the form is submitted.
Here's a simple example with return:
function foo1(){
console.log("Foo started...");
if(prompt("Type 1 to terminate right now or anything else to continue...") == "1"){
return; // Function will terminate here if this is encountered
}
console.log("Foo ending..."); // This will only be run if something other than 1 was entered
}
foo1();
And, here's an example with throwing an error (not something that is usually done):
function foo(){
console.log("foo started...");
for(var i = 0; i < 5; i++){
if(i === 3) { throw "I HATE 3!"; }
console.log(i);
}
console.log("foo ended...");
}
foo();
But, with Timers and Intervals, you'll need to call clearInterval() and/or clearTimeout() to stop them. These are different because, while some function may initiate the timer or interval, the timer runs outside of the JavaScript runtime environment as a WebAPI. For these, we have to send a message to the WebAPI that we want the timer to stop counting.
You say:
Now, this foo() function will run for unlimited time, when a specific
event occurs, let's say a stop button has been clicked, then I want to
stop this function.
But foo isn't running for an unlimited time. It's run once and then terminates. Then approximately 3 seconds later, the timer calls for the anonymous function you passed to it to be run and then that function terminates and approximately 3 seconds later the anonymous function runs again and then it again terminates and so on. The function isn't running consistently, the interval timer (the WebAPI that calls for the function to be invoked) is.
And what if the foo() function don't have the setInterval() method,
but just many lines of code that get executed and I want to stop it
from executing after a specific event.
Your question seems to imply that you want to stop a function that is currently executing when another event takes place. This can't really happen in JavaScript since JavaScript is a single-threaded environment. Any event can only be raised and handled after all other code is done processing. So, there really can't ever be a scenario like the one you mention, unless we are talking about asynchronous code. Asynchronous code is code that runs outside of the JavaScript runtime. With that kind of code, you can send a message to the WebAPI that is processing that external code that you would like to cancel/abort that processing and that is what we're doing when we call clearInterval().
See below:
document.getElementById("start").addEventListener("click", startInterval);
document.getElementById("stop").addEventListener("click", stopInterval);
// You'll need a variable to store a reference to the timer
var timer = null;
function startInterval() {
// Then you initilize the variable
timer = setInterval(function() {
console.log("Foo Executed!");
}, 1500);
}
function stopInterval() {
// To cancel an interval, pass the timer to clearInterval()
clearInterval(timer);
}
<button type="button" id="start">Start</button>
<button type="button" id="stop">Stop</button>
For that use return; in the place you want to kill the process

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.

jQuery while object.hasClass

I'm trying to find a way to stop a function at a certain point until something does not have a specific class anymore. I cannot change the place where this class is being assigned and removed because it's a plugin.
I was thinking of doing something like this
function DoSomething() {
while ($('div.divControl').hasClass('playing'))
{
//Wait here
}
};
Is this the correct way to go?
This will block so the element will never be changed, as no other code will execute.
What you need to use is an interval:
var interval = setInterval(DoSomething, 500);
function DoSomething() {
if ($('div.divControl').hasClass('playing'))
{
// Do something
clearInterval(interval);
}
};
This will execute the function every half second. The interval will be cancelled after the function succeeds.
No, that will just hang the browser as it goes into an infinite loop.
Your best bet (as best I can think at the moment anyhow) is to do a setTimeout on the function and have it check to see if it your div still has the class every quarter of a second or so.
Still, not nice at all =[

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);

Watching setTimeout loops so that only one is running at a time

I'm creating a content rotator in jQuery. 5 items total. Item 1 fades in, pauses 10 seconds, fades out, then item 2 fades in. Repeat.
Simple enough. Using setTimeout I can call a set of functions that create a loop and will repeat the process indefinitely.
I now want to add the ability to interrupt this rotator at any time by clicking on a navigation element to jump directly to one of the content items.
I originally started going down the path of pinging a variable constantly (say every half second) that would check to see if a navigation element was clicked and, if so, abandon the loop, then restart the loop based on the item that was clicked.
The challenge I ran into was how to actually ping a variable via a timer. The solution is to dive into JavaScript closures...which are a little over my head but definitely something I need to delve into more.
However, in the process of that, I came up with an alternative option that actually seems to be better performance-wise (theoretically, at least). I have a sample running here:
http://jsbin.com/uxupi/14
(It's using console.log so have fireBug running)
Sample script:
$(document).ready(function(){
var loopCount = 0;
$('p#hello').click(function(){
loopCount++;
doThatThing(loopCount);
})
function doThatOtherThing(currentLoopCount) {
console.log('doThatOtherThing-'+currentLoopCount);
if(currentLoopCount==loopCount){
setTimeout(function(){doThatThing(currentLoopCount)},5000)
}
}
function doThatThing(currentLoopCount) {
console.log('doThatThing-'+currentLoopCount);
if(currentLoopCount==loopCount){
setTimeout(function(){doThatOtherThing(currentLoopCount)},5000);
}
}
})
The logic being that every click of the trigger element will kick off the loop passing into itself a variable equal to the current value of the global variable. That variable gets passed back and forth between the functions in the loop.
Each click of the trigger also increments the global variable so that subsequent calls of the loop have a unique local variable.
Then, within the loop, before the next step of each loop is called, it checks to see if the variable it has still matches the global variable. If not, it knows that a new loop has already been activated so it just ends the existing loop.
Thoughts on this? Valid solution? Better options? Caveats? Dangers?
UPDATE:
I'm using John's suggestion below via the clearTimeout option.
However, I can't quite get it to work. The logic is as such:
var slideNumber = 0;
var timeout = null;
function startLoop(slideNumber) {
//... code is here to do stuff here to set up the slide based on slideNumber...
slideFadeIn()
}
function continueCheck() {
if (timeout != null) {
// cancel the scheduled task.
clearTimeout(timeout);
timeout = null;
return false;
} else {
return true;
}
};
function slideFadeIn() {
if (continueCheck){
// a new loop hasn't been called yet so proceed...
$mySlide.fadeIn(fade, function() {
timeout = setTimeout(slideFadeOut,display);
});
}
};
function slideFadeOut() {
if (continueCheck){
// a new loop hasn't been called yet so proceed...
slideNumber=slideNumber+1;
$mySlide.fadeOut(fade, function() {
//... code is here to check if I'm on the last slide and reset to #1...
timeout = setTimeout(function(){startLoop(slideNumber)},100);
});
}
};
startLoop(slideNumber);
The above kicks of the looping.
I then have navigation items that, when clicked, I want the above loop to stop, then restart with a new beginning slide:
$(myNav).click(function(){
clearTimeout(timeout);
timeout = null;
startLoop(thisItem);
})
If I comment out 'startLoop...' from the click event, it, indeed, stops the initial loop. However, if I leave that last line in, it doesn't actually stop the initial loop. Why? What happens is that both loops seem to run in parallel for a period.
So, when I click my navigation, clearTimeout is called, which clears it.
What you should do is save the handle returned by setTimeout and clear it with clearTimeout to interrupt the rotator.
var timeout = null;
function doThatThing() {
/* Do that thing. */
// Schedule next call.
timeout = setTimeout(doThatOtherThing, 5000);
}
function doThatOtherThing() {
/* Do that other thing. */
// Schedule next call.
timeout = setTimeout(doThatThing, 5000);
}
function interruptThings() {
if (timeout != null) {
// Never mind, cancel the scheduled task.
clearTimeout(timeout);
timeout = null;
}
}
When a navigation element is clicked simply call interruptThings(). The nice part is that it will take effect immediately and you don't need to do any polling or anything else complicated.

Categories