I can't figure out why setTimeout is being called multiple times in my code.
Here's a snippet of the code with what I thought was irrelevant removed:
let dead;
setup()
{
dead = false;
}
draw()
{
if(fell == true)
{
dead = true;
}
mechanics();
}
function mechanics()
{
let triggerVar;
if(dead == true)
{
triggerVar = 1;
dead = false;
}
if(triggerVar == 1)
{
setTimeout(resetG, 1500);
triggerVar = 0;
}
}
function resetG()
{
lives -= 1;
position = 0;
}
I can't tell what I'm doing wrong because whenever the character dies and setTimeout is called, it is actually not only called after the delay but also for the exact same duration after it is triggered. So in this case it is triggered first after 1500 millis and then every frame for another 1500 millis.
I managed to find the problem, which was not with the code I posted. The problem was that the constructor code that makes the object that changes dead to true if certain conditions are met was being called every frame from the moment it triggered death until the first instance of setTimeout kicked in, which means setTimeout was called every frame for 1500 milliseconds.
Chances are that you mechanics() function is called multiple times, you may give a variable to the settimeout like:
let timeoutID= setTimeout(resetG, 1500);
And in the proper place to clear it, for example after lifecycle
clearTimeout(timeoutID);
Related
Im developing a game in JavaScript in which the user needs to give a key input (press spacebar) when a clock hand moves slightly more than usual.
Currently, I am using a setTimeout function that gives the user 1 second to give a key input after the clock hand has ticked (rotated by 10 degrees).
If the user correctly presses space when the clock hand moves more than usual (15 degrees), an indicator will flash green, otherwise it will flash red.
The problem I am running into is that once the user gives an input within 1 second of the hand moving, the indicator will not flash until AFTER that 1 second has passed (ie, if the user gives an input after 0.4 seconds, the indicator will not flash until 0.6 later)
I know this is because the indicator is set up in my setTimeout fuction, which will only execute the code after 1 second. I have tried to test for the user input outside of the setTimeout function but that way the user does not get 1 second to give a response.
I was wondering if there is a way around this problem or a better way to approach this?
//Get input after clock tick
setTimeout(() => {
if (irregular_tick && space_pressed) {
flashScreenGreen();
}
if (!(space_pressed) && irregular_tick) {
flashScreenRed();
}
},1000);
Thanks for any help!
You'll need to keep a reference to your timer outside of the setTimeout callback and add a listener for the keypress with an interrupt callback which will clear the timeout if all conditions are met.
let timer = null;
let space_pressed = false;
function reset() {
timer = null;
space_pressed = false;
}
function interruptHandler(e) {
if (timer !== null) { // only look for spacebar if timer is running
space_pressed = e.key === ' ';
if (irregular_tick && space_pressed) {
// clear timeOut if successful
clearTimeout(timer);
reset();
flashScreenGreen();
}
}
}
document.body.addEventListener('keyup', interruptHandler);
timer = setTimeout(() => {
if (!space_pressed && irregular_tick) {
flashScreenRed();
}
// reset timer at end of callback ready for next run
reset();
}, 1000);
As a side note it looks like you've defined two separate flashScreenGreen() and flashScreenRed() functions. I'm guessing that they have similar if not identical logic. If that is the case you might want to consider defining a single utility flashScreen() function which accepts a color as a parameter.
function flashScreen(color) {
// logic utilizing 'color'
}
// usage
flashScreen('green');
flashScreen('#FF0000'); // red as hex
I think the clearTimeout function will help you here
// Hold the reference to the timer
const timeoutId = setTimeout(() => {
if (irregular_tick && space_pressed) {
flashScreenGreen();
//You can use the clearTimeout function to end the timer
clearTimeout(timeoutId);
}
if (!(space_pressed) && irregular_tick) {
flashScreenRed();
//clear timeout, if you need it here too
clearTimeout(timeoutId);
}
},1000);
I keep trying to post a question and it keeps getting closed as a duplicate, the last time it was because I forgot to add some code. However, with a little more testing I discovered that the problem is not what I initially thought it was. Here is my code:
class GameTimer {
construtor() {
this.interval = undefined;
this.time = 0; // seconds
}
start(seconds) {
this.time = seconds;
this.interval = setInterval(this.tick.bind(this), 1000);
}
tick() {
this.time--;
// for testing
console.log(this.time);
}
getTime() {
return this.time;
}
stop() {
clearInterval(this.interval);
}
reset() {
this.time = 0;
}
}
// Test
var gameTimer = new GameTimer();
var currentTime = 10;
gameTimer.start(currentTime );
while (gameTimer.getTime() > 0) {
if (gameTimer.getTime() != currentTime) {
console.log(gameTimer.getTime());
currentTime = gameTimer.getTime();
}
// This is here only to see if I am going into the loop
console.log("In loop: " + gameTimer.getTime());
}
At first, I thought tick was not getting called because I was getting stuck in the loop. In my original code I did not have the .bind(this) on with this.tick in the class to setInterval. I added that and had the same problem. I tried to post that but was kicked out again because I forgot to add it to the code the I copied into the question (I copied the old code instead of the new). Anyway, with this change it still does not work. I placed console.log(this.time) and the time was never output, again making me think that tick was not getting called. However, just to see what would happen, I commented out the while loop and sure enough tick is getting called. So even though there was a problem with the binding, which I appreciate those who pointed this out, now there seems to be another problem. Why will it not work inside the loop?
In some cases the function below will not call clearInterval even though the question timer goes lower than 0. My console reports 0..-1..-2..etc. and even though it meets the condition it does not stop the timer. This is using socket.io so I'm not sure if it's some sort of timing issue because it seems to happen randomly.
EDIT: So this code is inside a function the function is called from two different places. The var questionTimer is declared globally in my app.js file because I use it in another function. If questionBeginCountdown is called again before clearInterval is called would that mean there are two separate instances of setInterval running (ie. the first instance of setInterval calls clearInterval but because the returned value for questionTimer is different it fails)?
function questionBeginCountdown()
{
questionTimer = setInterval(function() {
questionTimerCount--;
playerSpc.emit('update interval', questionTimerCount);
gameSpc.emit('update interval', questionTimerCount);
guestHost.emit('update interval', questionTimerCount);
console.log('timer count: ' + questionTimerCount);
if (questionTimerCount <= 0) {
console.log('stopping timer.');
clearInterval(questionTimer);
questionTimesUp(); //calls a function that emits various things
}
}, 1000);
}
If questionBeginCountdown is called again before clearInterval is called would that mean there are two separate instances of setInterval running?
I believe it does. Maybe rethink why you're calling it twice to begin with.
One quick fix is to, inside questionBeginCountdown check if questionTimer is already set before setting the interval. Then somewhere down the line when the timer is done and after clearing the interval, set questionTimer to null.
Another quick fix, if you need two timers independent of each other, is to make questionTimerCount local to the questionBeginCountdown, but that depends on what you're doing with the timer.
Edit
An example of what I said about.
let questionTimer,
questionTimerCount = 2;
function questionBeginCountdown()
{
if (questionTimer)
return
questionTimer = setInterval(function() {
questionTimerCount--;
//playerSpc.emit('update interval', questionTimerCount);
//gameSpc.emit('update interval', questionTimerCount);
// guestHost.emit('update interval', questionTimerCount);
console.log('timer count: ' + questionTimerCount);
if (questionTimerCount <= 0) {
console.log('stopping timer.');
clearInterval(questionTimer);
//questionTimesUp();
questionTimer = null;
}
}, 1000);
}
// calling it 4 times doesn't set 4 intervals
questionBeginCountdown();
questionBeginCountdown();
questionBeginCountdown();
questionBeginCountdown();
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
So I'm new here, apologies if this is a basic question but I can't see it answered anywhere else.
I'm trying to write a webapp which shows an animation (using pixijs), and afterwards displays a div requesting a response. My issue is that because the animation is handled using requestAnimationFrame the animation occurs asynchronously, and so the response and animation phases occur simultaneously (the div appears instantly and obscures the animation).
Now I've looked around and the consensus seems to be to use a callback function, which only gets triggered after all the asynchronous work has been performed, allowing that serial progression.
HOWEVER, requestAnimationFrame takes the form
requestAnimationFrame(update_screen_objects),
but breaks when I try to do:
requestAnimationFrame(update_screen_objects(response_phase_callback))
as clearly requestAnimationFrame doesn't like being passed a function that itself has a callback. So my question is: what should I do to the animation loop to ensure that subsequent functions execute AFTER the animation is complete?
Thanks!
EDIT
This is an example of the code in the above form that doesn't work.
function animate(callback) {
var finished = false;
if ((new Date().getTime()) < adaptTime){
runFlicker(false);
} else if ((new Date().getTime()) < judgeTime){
hideAdaptors();
} else if ((new Date().getTime()) > judgeTime){
stage.visible = false;
finished = true;
}
renderer.render(stage);
if (!finished){
requestAnimationFrame( animate(callback) ); //Ensures it will keep looping
//requestAnimationFrame(animate); //This does work, but my issue still persists
} else {
callback(); //By the time callback() is required, you can't access it as requestAnimationFrame doesn't accept animate() with a function passed to it.
}
}
No need for a complex wrapper, just do:
requestAnimationFrame(update_screen_objects.bind(window, response_phase_callback))
This "currys" the update_screen_objects function by partially applying some arguments. The result of .bind(context, arg1) is a function that when called, only takes any arguments that aren't already bound, e.g arg2, arg3, etc.
Try this out. You basically need to generate that step (animate) function with the callback, instead of passing in the results of your call to animate, which would be undefined.
function generateAnimation(callback) {
function step() {
var finished = false;
var now = (new Date()).getTime();
if (now < adaptTime) {
runFlicker(false);
} else if (now < judgeTime) {
hideAdaptors();
} else if (now > judgeTime) {
stage.visible = false;
finished = true;
}
renderer.render(stage);
if (!finished) {
requestAnimationFrame(step);
} else {
callback();
}
}
return step;
}
// Usage:
requestAnimationFrame(generateAnimation(callback));