This is for frontend Javascript linked to an HTML file if that's relevant. I tried using IIFE for this problem and a bunch of things broke, so I'd like to avoid trying again if possible. I declared a timer that I want to stop conditionally (i.e. backend sends front end a message to stop or timer ticks for thirty seconds, whichever comes first), but I'm not sure how to do this without globally declaring a timer variable.
Here's some dummy code because the actual thing is around 300 lines:
const startTimer = ()=>{
let time = 30
const timer = setInterval(()=>{
if(time === 0){
clearInterval(timer)
}
}
},1000)
}
startTimer()
socket.on('stop-timer', ()=>{
//I want to clear the timer when this gets emitted.
})
How would I avoid declaring a global timer? Is it really that bad to do so?
You could create a simple class (I assume you use ES6 by the look of your code) that will expose cancel method to clearInterval like this:
class Timer {
constructor(time) {
this.time = time;
this._interval = null;
}
start() {
this._interval = setInterval(...);
}
cancel() {
clearInterval(this._interval);
this._interval = null;
}
}
const timer = new Timer(30);
timer.start();
socket.on('stop-timer', timer.cancel);
Related
I'm attempting to make a basic count-down timer in React. It should start at 30 seconds, and count down by 1 each second. When clicking the button, the timer should restart at 30 and begin the countdown again.
This seems simple enough, and it's printing to the console exactly as I expect it to. However, as soon as I try to update state with my timer so I can render the countdown on-screen (uncomment the commented line below) the console.log duplicates, the render doubles, and I seem to have two states running simultaneously. I'm not sure what to do about this.
Any help is greatly appreciated!
const [seconds, setSeconds] = useState(30)
let interval = null
function startTimer() {
stopTimer()
let start = 30
interval = setInterval(() => {
// setSeconds(start)
console.log('start: ', start)
start--
}, 1000)
}
function stopTimer() {
clearInterval(interval)
}
return (
<p>{seconds}s</p>
<button onClick={startTimer}>Start</button>
)
I've looked around to see what I could find myself before posting. I read through a number of articles on React and setInterval, and watched some tutorials, but couldn't find what I was looking for. I attempted to rewrite the code in different ways but always ended with the same result.
There are multiple things to say, like why use async/await when there is nothing to await for, why use a local variable start = 30 when you just want to decrease your seconds count and why you declare the interval in the function body. A React functional component will run all its code and in your case do let interval = null everytime it rerenders. You have to store the interval somewhere else, like here as a global variable. Moreover, when you create the setInterval, it won't have access to the new seconds count. What you can do is use the arrow function form inside your setState function. Doing so, you will get the right seconds variable.
Maybe the code below will help you find out what's wrong:
let interval = null
function App(props) {
const [seconds, setSeconds] = React.useState(30)
function startTimer() {
stopTimer()
interval = setInterval(() => {
setSeconds((seconds) => seconds - 1)
}, 1000)
}
function stopTimer() {
clearInterval(interval)
setSeconds(30)
}
return (<button onClick={startTimer}>{seconds}</button>)
}
Thanks for the help folks!
Turns out, regardless of how I assign the state to seconds (arrow function or a separate variable) the cause of the issue here was the placement of the 'interval' variable.
Thomas's answer was correct. Moving it outside of the functional component rather than inside corrected the issue. If the variable was within the function the interval seemed like it didn't fully clear, just paused, and then there were two intervals running simultaneously.
Here's the final code, functioning as expected.
import { useState } from "react"
let interval = null
export default function app() {
const [seconds, setSeconds] = useState(30)
function startTimer() {
stopTimer()
interval = setInterval(() => {
setSeconds((seconds) => seconds - 1)
}, 1000)
}
function stopTimer() {
clearInterval(interval)
setSeconds(30)
}
return (
<p>{seconds}s</p>
<button onClick={startTimer}>Start</button>
</div>
)
}
I have a small issue clearing an interval with Javascript.
I've searched for the mistake that I've made but I couldn't find any.
Variable declarations:
let i, interval;
let currentLoops = 0;
let settings = {
amount:0,
loops:0,
speed:0};
Where i make my interval:
$('#start').click(()=>{
getSettings();
let interval = setInterval(setColorGrid, settings.speed);
});
How i tried to clear it:
if(currentLoops == settings.loops){
clearInterval(interval);
console.log("interval cleared");
}
If you have any ideas/suggestions for this comment below.
Remove the let part from the event handler. It causes to create a handler function scoped interval variable which shadows the outer interval variable.
$('#start').click(() => {
getSettings();
interval = setInterval(setColorGrid, settings.speed);
});
I am trying to make a simple game like: www.aimbooster.com to test my javascript knowledge but I have come into a problem. Basically I want to have a timer that counts the amount of time the user has got but I can't use the setInterval method because then the program has to wait a whole second before anything else happens which is not what I want. What I want is this timer process to run in the background. I think this is commonly named as "Threading". How would I go about this?. I'd want something along the lines of:
function startTimer() {
while (stop == false) {
setInterval(function() {time++};1000);
}
}
function startGame() {
startTimer();
//MY GAME STARTS HERE
}
If I do this at the moment, the startTimer function will continually go and I won't be able to do anything else. I want startTimer to be a subproccess while the main process is the game.
while (stop === false) {
setInterval(function() {time++};1000);
}
this is a infinite loop, and you are calling setInterval infinite time and the eventloop doesn't have a chance to pop the callback to the stack to increase the time variable.
I would like to save the timer variable, and use clearInterval to stop the timer
function Game() {
}
Game.prototype.start = function() {
this.timer = setInterval(function() {
// do your stuff
}, 1000);
// start game
}
Game.prototype.stop = function() {
clearInterval(this.timer);
}
You could do something like:
function startTimer() {
setInterval(function() {while (stop == false) time++}, 1000);
}
I am doing some long polling (ajax) and I am looping the following portion of code.. There is code being executed above and below. This code is part of an internal messaging system. A certain portion of the page wil blink when a message arrives. If the user checks the message, it will remove the dash_notify from the JSON response, which needs to turn off the blinking. See below:
if (data.dash_notify == '1') {
var x = '#dash_notif_blink';
function blinking(x) {
timer = setInterval(blink, 10);
function blink() {
x.fadeOut(400, function () {
x.fadeIn(400);
});
}
}
console.log("initiate_dash");
blinking($(x));
} else if (!data.dash_notify) {
console.log("good");
clearInterval(timer);
}
The following JSON response that gets sent to this code is:
{"current_date_time":"January 8, 2013 - 4:02 pm","dash_notify":"1"}
It understand the initial blink IF the above data gets passed. If the following gets passed:
{"current_date_time":"January 8, 2013 - 4:02 pm"}
Then it throws an error:
Uncaught ReferenceError: timer is not defined
I cannot figure out how to fix the "else" portion working properly. If the code is initiated when the full dash_notify:1 response is sent, it works perfect. The button will blink, then if the user checks the message, it will no longer send dash_notify:1 and the button stops blinking. But if the code is initiated when dash_notify:1 is NOT set, it doesn't know what to do with the ClearInterval.
Basically I need the else portion fixed.
I have tried using different typeOf === undefined snippets, but it doesn't work.
Any help is appreciated.
Thank you!
EDIT:
This is currently working.. Timer is now defined above the statement
if(data.dash_notify == '1'){
var x = '#dash_notif_blink';
console.log("initiate_dash");
blinking($(x));
}else if (typeof timer != "undefined" && timer) {
clearInterval(timer);
}
}
This is working, but sometimes it trys to kill the timer but it doesn't actually do it. This happens every so often.
Looks like it's not working because timer doesn't exist outside your inner blinking function. I'm making an assumption here that you don't have var timer; somewhere outside the blinking function, which is strongly likely given the error you're getting.
Why this is happening:
If I'm right, and you're not declaring timer anywhere else in your code, then var timer is being implicitly added to the beginning of the blinking function:
function blinking(x) {
var timer;
timer = setInterval(blink, 10);
function blink() {
x.fadeOut(400, function () {
x.fadeIn(400);
});
}
}
That makes timer a local variable inside blinking. Since you never pass it out of the closure, it doesn't exist once you're outside that function. So either you need to pull timer into the outer context (option 1) or make it available from inside blinking (option 2).
What to do:
If you want access to timer outside of that closure, you'll have to do one of two things:
1: Declare timer outside of blinking:
var timer = null;
if (data.dash_notify == '1') {
var x = '#dash_notif_blink';
function blinking(x) {
//etc...
2: Make it the return value of blinking:
var t;
if (data.dash_notify == '1') {
var x = '#dash_notif_blink';
function blinking(x) {
var timer = setInterval(blink, 10); //note the var keyword for best practice
function blink() {
x.fadeOut(400, function () {
x.fadeIn(400);
});
}
return timer;
}
console.log("initiate_dash");
t = blinking($(x));
} else if (!data.dash_notify) {
console.log("good");
clearInterval(t);
}
Either one of these will work, and is more or less the same in terms of polluting the outer namespace. I prefer Option 2, because I feel like it's easier to work with a local variable until you need to return it.
Edit:
Your comment said the loop runs infinitely, which means you're creating a brand new interval and reassigning the timer variable every time. This is a separate problem from the one I described above. The old interval is still out there, timer just doesn't point to it anymore. So how can clearInterval(timer) clear out all those intervals? It can't, it can only clear the most recent one.
Basically, you've got a whole bunch of timers all trying to make the thing blink at once.
How you deal with this depends on what you're trying to do. The simplest thing would be to keep no more than one interval running at once, which means you have to clear timer every time.
//same as option 1 above except for `clearInterval(timer)` at the
//beginning of `blinking`
var timer = null;
if (data.dash_notify == '1') {
var x = '#dash_notif_blink';
function blinking(x) {
clearInterval(timer);
timer = setInterval(blink, 10);
If you need multiple timers running, you'll have to keep track of them all in an array or something:
var timers = [];
//...
function blinking(x) {
timers.push(setInterval(blink, 10));
//...
} else if (!data.dash_notify) {
timers.forEach(function(timer) {
clearInterval(timer);
});
}
Not sure what you did wrong with your typeof check since you did not actually show the whole code, but it should look something like this:
if (typeof timer != "undefined" && timer) {
clearInterval(timer);
}
Basically define your variable timer before you enter your checking procedure (loop?):
var timer;
... some code ...
if ( data.dash_notify && data.dash_notify == '1') {
...
} else if (!data.dash_notify) {
clearInterval(timer);
}
You can call clearInterval( whatever ) without any consequences. Even if whatever is null, undefined, string and so on. Just make sure timer exist.
Passing an invalid ID to clearTimeout does not have any effect (and
doesn't throw an exception). (MDN)
You're getting that error because timer is only declared/initialized in the blinking function. In the place where you call clearInterval(timer), timer doesn't exist.
This is now working beautifully.
*Thank you to everyone who helped!*
if(data.dash_notify === '1' && t === null ){
var x = '#dash_notif_blink';
function blinking(x) {
var timer = setInterval(blink, 10); //note the var keyword for best practice
function blink() {
x.fadeOut(400, function () {
x.fadeIn(400);
});
}
return timer;
}
console.log('initiate_dash_alert');
// Data is passed. Parse to see if dash alert should be called. Secondary protection for
// multiple timer creation.
if(t){return;}else{t = blinking($(x));}
}else if (!data.dash_notify){
clearInterval(t);
console.log('clear_dash_alert');
t = null;
}else{
console.log(t);
console.log('no_push_events');
}
We have started a timer with the following code. Is it possible to make window.setInterval method fail, if another timer is active on the method passed to the window.setInterval method?
GInterValId = window.setInterval("AutoRefresh()",parseInt(GRefreshInterval));
what you do is set up a system for this. Create an object that handle all your Timers:
var Timer = function () {
var currentTimer;
this.setTimer = function (func,time) {
if (currentTimer) {
alert("one timer already set");
}else {
currentTimer = setInterval(func,time);
}
}
this.stopTimer = function () {
clearInterval(currentTimer);
currentTimer = null;
}
}
Now you can use this code:
function doSomething() {...} // function example
function doSomethingElse() {...} // function example
var MyTimer = new Timer();
MyTimer.setTimer(doSomething,1000); // this will run
MyTimer.setTimer(doSomethingElse,1000); // this will not run
The second one will not run because the other one is active.
to make it work you have clear the first one.
MyTimer.stopTimer(); // will clear the current timer then you can run another one
MyTimer.setTimer(doSomethingElse,1000); // will run perfectly
If you keep track of the result result from window.setInterval(), you can stop the previous interval timer at any point.
var GInterValId = setInterval(AutoRefresh, parseInt(GRefreshInterval));
Then, when you want to reset it:
if (GInterValId) {
clearInterval(GInterValId);
}
GInterValId = setInterval(AutoRefresh, parseInt(GRefreshInterval));
Also note that I'm not passing a string to setInterval, but an actual JS function reference.
Or, if you just want to prevent another interval getting set:
var GInterValId = null; // initialize
// then, when you want to set it, check to see if it's already been set
if (GInterValId) {
GInterValId = setInterval(AutoRefresh, parseInt(GRefreshInterval));
}