I have a setInterval calling a loop which displays an animation.
When I clearInterval in response to a user input, there are possibly one or more loop callbacks in queue. If I put a function call directly after the clearInterval statement, the function call finishes first (printing something to screen), then a queued loop callback executes, erasing what I wanted to print.
See the code below.
function loop() {
// print something to screen
}
var timer = setInterval(loop(), 30);
canvas.onkeypress = function (event) {
clearInterval(timer);
// print something else to screen
}
What's the best way to handle this? Put a delay on the // print something else to screen? Doing the new printing within the loop?
Edit: Thanks for the answers. For future reference, my problem was that the event that triggered the extra printing was buried within the loop, so once this executed, control was handed back to the unfinished loop, which then overwrote it. Cheers.
You could also use a flag so as to ignore any queued functions:
var should;
function loop() {
if(!should) return; // ignore this loop iteration if said so
// print something to screen
}
should = true;
var timer = setInterval(loop, 30); // I guess you meant 'loop' without '()'
canvas.onkeypress = function (event) {
should = false; // announce that loop really should stop
clearInterval(timer);
// print something else to screen
}
First of all, you probably meant:
var timer = setInterval(loop, 30);
Secondly, are you sure calling clearInterval does not clean the queue of pending loop() calls? If this is the case, you can easily disable these calls by using some sort of guard:
var done = false;
function loop() {
if(!done) {
// print something to screen
}
}
var timer = setInterval(loop(), 30);
canvas.onkeypress = function (event) {
clearInterval(timer);
done = true;
// print something else to screen
}
Related
I did some research and found busy wait solutions. I don't want the processor to execute a loop in the script until I get a keypress event through JQuery.
How can I pull of something like sleepUntilKeyPress(), which is a function like sleep() in C but instead of waiting some especific time it waits for a keypress?
You're thinking backwards. Your function shouldn't wait for a keypress to do something, the function should do something whenever a key is pressed. Maintain your data state in a scope that your key callback can reach and update your state on keypress.
You're trying to say:
while(!keypress){
dontDoSomething();
}
You should be doing something more like:
on(keypress, doSomething);
Consider this idea:
var counter, ispressed, el;
// Bad
counter = 0;
ispressed = false;
el = document.getElementById("element");
el.addEventListener("mousedown", function () {
ispressed = true;
});
el.addEventListener("mouseup", function () {
ispressed = false;
});
while (1) {
if (ispressed) {
counter += 1;
}
}
// Better
el.addEventListener("click", function () {
counter += 1;
});
Let's forget about the fact that that while will lock up the program, (I don't even think the event listeners will trigger and I'm pretty sure the DOM will lock up), you're asking your program to waste time constantly checking to see if you're pressed a button yet. Rather than do that, let the browser check for you with the built in addEventListener() and do what you would normally do when you find out that the button has been pressed.
var interval = window.setInterval(function () {
// hang around and do nothing
}, 1000);
document.onkeypress = function () {
if (/* some specific character was pressed */) {
window.clearInterval(interval);
// do some other thing, other thing
}
};
I have this code:
function toStop(){
while(true){}
}
toStop();
Now, how can I stop this? Or how can I kill the current thread if this function call is somewhere in the setInterval running thread? Example:
var id = setInterval(function(){
toStop();
}, 1000);
//stop thread/timer with id here.
clearInterval doesn't work because it waits until the function call ends.
Thanks!
"Can I stop the execution of a function from outside that function?"
No, you can't programmatically.
JavaScript is single-threaded and if you run a piece of code that makes it infinitely busy, such as while(true);, then nothing else will ever be able to execute.
Calling such a piece of code within setTimeout or setInterval will have the same result, since the callback of these gets executed in the only thread we have as well.
However, you can create a timed recurring execution using setInterval or setTimeout, which can be stopped.
var timerId = setInterval(function () {
//Process an iteration of the loop in here
//If you cause an infinite loop in here, you will have the same issue
}, 50);
//stop the timer after ~3 seconds
setTimeout(clearInterval.bind(null, timerId), 3000);
Notes:
4 is the lowest interval that could be honored as specified in the SPEC.
setInterval will stack if the callback takes more time to execute than the specified interval. For that reason I never use setInterval and always use setTimeout.
Timer intervals are not guaranteed to be accurate
e.g. with setTimeout
var stopProcessing = startProcessing();
//Stop processing after ~3 seconds
setTimeout(stopProcessing, 3000);
function startProcessing() {
var timerId;
!function process() {
//Do some processing
//Continue processing in ~50 ms
timerId = setTimeout(process, 50);
}();
return function () { clearTimeout(timerId); }
}
Instead of an infinite loop, just use an if statement and wrap it in an interval:
var shouldContinue = true;
var interval = 0;
function toStop() {
if (interval == 0) {
interval = setInterval(function() {
if(shouldContinue) {
...
}
else {
clearInterval(interval);
interval = 0;
}
}, 200); // Or whatever interval makes sense
}
}
toStop();
// ...
shouldContinue = false;
See this principle in action here.
No, you can't programmatically, as #plalx said but you could try this: declaring a binding outside and check on that to continue or stop the loop:
let letMeGoOut;
function toStop(){
while(letMeGoOut != false)
}
toStop();
Here, I've created a function on mouseover that triggers a loop changing the opacity of the h1. It goes on till the mouse cursor moves out and is over something else in the page.
Here is the example: https://codepen.io/Mau-Di-Bert/pen/VqrRxE
var intervalHandle ;
var currentOpacity=0;
function beginAnimate() {
current_question_div = document.getElementById(current_question);
intervalHandle = setInterval(function(){
animateBox(current_question_div)
}, 200);
alert("Hellow ashik !");
}
function animateBox(current_question_div) {
current_question_div.style.backgroundColor = "lightblue";
currentOpacity = currentOpacity + .091;
current_question_div.style.opacity = currentOpacity;
if(currentOpacity > 1) {
alert("END");
clearInterval(intervalHandle);
}
}
<P onclick="beginAnimate">Click</p>
Everything is ok, but alert("Hellow ashik !"); working while interval is in execution. I want to open the alert("Hellow ashik !") When the clearInterval occurred. Means now other JavaScript code is executing in parallel while interval is also executing. I need to execute only one thread at a time. Please help. Is there a way to execute this code one by one. Thanks.
This code should accomplish what you want:
var intervalHandle,
currentOpacity = 0;
function beginAnimate()
{
current_question_div=document.getElementById(current_question);
/* call any other functions that need to run before the animation starts here */
alert("Hellow ashik !");
intervalHandle = setInterval(function(){
// note 'endAnimate()' is passed to the 'animateBox()' function as a callback
animateBox(current_question_div, endAnimate)
}, 200);
/* call any functions that can run while the animation is running here */
}
function endAnimate() {
alert("The end!");
/* call any other functions that need to be run after the animation here */
}
function animateBox(current_question_div, callback) {
current_question_div.style.backgroundColor = "lightblue";
currentOpacity = currentOpacity + .091;
current_question_div.style.opacity = currentOpacity;
if(currentOpacity>1)
{
alert("END");
clearInterval(intervalHandle);
if (callback && typeof(callback) === 'function') {
callback();
}
}
}
<P onclick="beginAnimate">Click</p>
JavaScript is a single threaded language -- ignoring WebWorkers or other workarounds. However, it is asynchronous and because of that you often see behavior that looks like threading. One way to work around these issues is to use the callback pattern.
In the callback pattern, you pass a function to be called later when your asynchronous function completes. JavaScript makes this simple as functions are first class objects and can be passed or assigned to just as easily as a number.
In a browser, javaScript has a single-threaded nature, thus it executes on a single thread per window. This means that the execution diagram is one dimensional: javaScript can only ever execute one piece of code at a time.
The browsers are event-driven. Most events are asynchronous, such as mouse clicks, key press, and timers. They are only run when there is an opening in the execution; and in the meantime, they are forced to get into the event queue waiting for execution.
<p id="test"></p>
document.getElementById("test").innerHTML += "a";
var intervalHandle = setInterval(function(){
callback();
}, 200);
document.getElementById("test").innerHTML += "c";
function callback() {
document.getElementById("test").innerHTML += "b";
//here where should be located all the code supposed to be executed after the timeinterval.
clearInterval(intervalHandle);
//here where should be located all the code supposed to be executed after the clearInterval.
}
Check this link jsfiddle to see the working example.
In the example above, the javascript thread starts setting the content of p to "a", then delays the execution of callback. This goes to the Event queue on the nearest timer tick after 200ms. And then continue execution, setting the content of p to "c".
So if you want to execute something after the clearInterval, then you should place it after the clearInterval.
Hope it's useful!
I'm really confused how these work...the timeout does not seem to keep running it calls begin_anim once and then thats it....
So am hoping someone can see where i went wrong and explain how I implement this?
This is my code:
//test data:
//type = 'up';
//div = document.getElementById('theid');
//marginL = -400;
function timeout_begin(type,div,marginL){
setTimeout(begin_anim(type,div,marginL),1000);
}
function begin_anim(type,div,marginL){
if(type == 'up'){
if(marginL >= '-200'){
if(marginL > '-200'){
div.style.marginLeft = '-200px';
}
return false;
}
marginL += 2;
div.style.marginLeft = marginL+'px';
}
return false;
}
Hope you can help!
You're looking for setInterval!
Also, it's probably better to pass an actual function in, and you can hold a reference to the loop so you can stop it running later if you want to:
var animationLoop = setInterval(function () {
begin_anim(type, div, marginL);
}, 1000);
clearInterval(animationLoop); // This would then stop the loop.
First, you want setInterval, not setTimeout
Second, you'll pass a reference to a function, not a call to a function. Something like:
function timeout_begin(type,div,marginL)
{
setTimeout(
function() {
begin_anim(type,div,marginL);
},
1000
);
}
setTimeout is supposed to call the function only once.
if you want to call the method repeatedly use setInterval(function(){}, 1000/*duration*/)
setTimeout is only expected to execute the function once after the given timeout. See the documentation here: http://www.w3schools.com/jsref/met_win_settimeout.asp
You're probably looking for setInterval (http://www.w3schools.com/jsref/met_win_setinterval.asp) which executes the code at the interval you set until clearInterval is called.
I am trying to create the following functionality in my javascript:
$("mySelector").each(function(){
// Do something (e.g. change div class attribute)
// call to MyFunction(), the iteration will stop here as long as it will take for myFunction to complete
});
function myFunction()
{
// Do something for e.g. 5 seconds
}
My question is how can I stop every iteration for the duration of the myFunction()?
No, that isnt possible. You'll have to code it differently, possibly with a setTimeout based on the current index of .each.
$("mySelector").each(function(i){
// Do something (e.g. change div class attribute)
// call to MyFunction(), the iteration will stop here as long as it will take for myFunction to complete
setTimeout(myFunction,i*5000);
});
function myFunction()
{
// Do something for e.g. 5 seconds
}
Edit: You can also do it with queuing: http://jsfiddle.net/9Bm9p/6/
$(document).ready(function () {
var divs = $(".test");
var queue = $("<div />");
divs.each(function(){
var _this = this;
queue.queue(function(next) {
myFunction.call(_this,next);
});
});
});
function myFunction(next) {
// do stuff
$(this).doSomething();
// simulate asynchronous event
var self = this;
setTimeout(function(){
console.log(self.id);
// go to next item in the queue
next();
},2000);
}
Here's a jsFiddle that I think will do what you need:
http://jsfiddle.net/9Bm9p/2/
You would just need to replace the selector with what you use.
The "loop" that is occurring will wait for myFunction to finish before moving on to the next element. I added the setTimeout inside of myFunction to simulate it taking a period of time. If you are using asynchronous things, such as an AJAX request, you would need to put the call to myFunction inside of the complete method...or in the callback of an animation.
But as someone already commented, if everything in myFunction is synchronous, you should be able to use it as you are. If you are looking for this process to be asynchronous, or if things in myFunction are asynchronous, you cannot use a for loop or .each().
(function () {
"use strict";
var step = 0;
var content = $("mySelector");
var max = content.length;
var speed = 5000; // ms
var handle = setInterval(function () {
step++;
if (step >= max) {
clearInterval(handle);
} else {
var item = content[step];
// do something
}
}, speed);
}());
setInterval will do it once-every-n-miliseconds, and clearInterval will stop it when you're done. This won't lock up the browser (provided your "do something" also doesn't). FRAGILE: it assumes that the results of $("mySelector") are valid for the duration of the task. If that isn't the case then inside do something then validate item again.