Flickering and glitching occuring when setInterval used in loop - javascript

function startTimer(duration, activity) {
function countdown() {
if ((duration - 1) > 0) {
duration--;
$("#timer").html(convertNumbers(duration))
} else {
if (activity === "session") {
startBreak();
} else {
startSession();
}
}
}
setInterval(countdown, 1000)
}
function startSession() {
var time = parseInt(($("#session_time").text()), 10);
startTimer(time * 60, "session");
$("#activity").html("Session");
}
function startBreak() {
var time = parseInt(($("#break_time").text()), 10);
startTimer(time * 60, "break");
$("#activity").html("Break");
}
The start button:
$("#start").click(function() {
startSession();
});
I am making a Pomodoro clock using setInterval that runs every second to decrement by one second. When the start button is pressed, the startSession() function grabs the time and calls the startTimer() function, which points to setBreak() when the duration is 0.
Ideally it would alternate between startSession and startBreak, but I get flickers and random numbers in the text (time left) set in "#timer" after the initial countdown. This might have something to do with not ending the Interval when startTimer() calls startBreak().

You never clear the initial interval, so when the break occurs, two intervals are running simultaneously.
setInterval returns an ID of the interval of which you can pass to clearInterval() to stop it again.
var timer = setInterval(function () {
console.log('A second has passed!');
}, 1000});
setTimeout(function () {
clearInterval(timer);
console.log('Seconds no longer passes');
}, 10 * 1000);
See documentation for setInterval and clearInterval for more information.

Related

How to use setInterval() and clearInterval() in the same function

I'm trying to create a function that starts and stops a timer. The starting is always on the click of a button, but the stopping can be due to the timer running down or the function being called again from another function.
This is what I have so far. Works perfect for what you see but I cannot figure out how to incorporate clearInterval() so that it stops when the game is won. The functioning calling timerCountdown is located in a different js file. I've read answers to similar questions but they are all seem to be doing it a little differently to where I can't make it work for my case
I do realize that I need to call clearInterval(count) but I don't know how to incorporate this into the function itself.
const timerCountdown = () => {
let count = setInterval(() => {
let timeLeft = timer.innerHTML
if (timeLeft > 0) {
timer.innerHTML = timeLeft - 1
} else {
gameOverMessage()
}
}, 1000)
}
You need to push the interval id in a global variable. Like that you can use another function to stop the interval when you want.
Like
let intervalId; // define `count` globaly
let timer = document.getElementById('timer')
const timerStop = () => {
clearInterval(intervalId)
}
const timerRestart = () => {
timerStop()
timer.innerHTML = 100;
}
const timerStart = () => {
timerStop(); // Stop timer by security, e.g. if you call `timerStart()` multiple times
intervalId = setInterval(() => {
let timeLeft = timer.innerHTML;
if (+timeLeft > 0) {
timer.innerHTML = +timeLeft - 1
} else {
timerRestart();
gameOverMessage()
}
}, 1000)
}
<div id="timer">100</div>
<div>
<button onclick="timerStart()">Start</button>
<button onclick="timerStop()">Pause</button>
<button onclick="timerRestart()">Restart</button>
</div>
setInterval makes a best effort to space the running of the callback according to the interval you specify. The thing is: in a game, what you actually want is the current state of the world to be printed to the screen in smooth and timely fashion. This is different to the behavior of setInterval, which knows nothing about the screen and is blindly called repeatedly.
For example: if you kick-off setInterval(foo, 100) for your game in a browser tab, and then navigate to another tab in your browser, wait ten seconds and then return to your game, your foo callback will be invoked about a hundred times in rapid succession as the queued callbacks are "drained". It is highly unlikely you want this behavior.
requestAnimationFrame is a better solution for this, because it is only called when (shortly before) your game is rendered - which is what you want.
In the following code a timer object is created by createTimer. The timer has start, stop and toggle methods.
The start method records when it was invoked and triggers requestAnimationFrame, supplying a callback called tick. Every time a tick occurs, we run some logic to see which (if any) callback to invoke.
If the time elapsed is greater than or equal to the duration of the timer, then the onTimeout callback is invoked and the timer is stopped.
If the time elapsed is smaller than the duration, but greater than or equal to the interval period, then we update the lastInterval and invoke the onInterval callback.
Otherwise we simply cue up another tick of the timer.
The stop method simply uses the request animation ID to cancel the timer with cancelAnimationFrame.
function createTimer() {
let rafId = null
function start({duration = 10000, interval = 1000, onInterval, onTimeout, onStop, startTime=performance.now(), lastInterval = startTime}) {
function tick(now=performance.now()) {
const elapsed = now - startTime
if (elapsed >= duration) {
cancelAnimationFrame(rafId)
rafId = null
return onTimeout()
}
if ((now - lastInterval) >= interval) {
lastInterval = now
onInterval({
duration,
elapsed
})
}
rafId = requestAnimationFrame(tick)
}
rafId = requestAnimationFrame(tick)
}
function stop() {
cancelAnimationFrame(rafId)
rafId = null
return onStop()
}
function toggle(...args) {
rafId ? stop() : start(...args)
}
const timer = {
start,
stop,
toggle
}
return timer
}
const timer = createTimer()
const onInterval = ({duration, elapsed})=>console.log(`Remaining: ${((duration - elapsed)/1000).toFixed(0)}`)
const onTimeout = ()=>console.log('Timed out.')
const onStop = ()=>console.log('Manually stopped.')
document.getElementById('btn').addEventListener('click', () => timer.toggle({
onInterval,
onTimeout,
onStop
}))
<button id="btn">Toggle Timer</button>
You could take a global variable intervalId and clear the interval timer, if won or if no time is avaliable.
var intervalId;
const timerCountdown = () => {
intervalId = setInterval(() => {
let timeLeft = timer.innerHTML
if (timeLeft > 0) {
timer.innerHTML = timeLeft - 1
} else {
clearInterval(intervalId);
gameOverMessage();
}
}, 1000)
},
won = () => {
clearInterval(intervalId);
// additional code
};

clearTimeout not working: parameter undefined(even though it's defined in the global scope)

My setTimeout() function works, but my clearTimeout() is not working. Even though I have an 'if' statement that's supposed to run the clearTimeout function once my variable 'secs' is less than 0, the timer keeps counting down into negative numbers. When I type my variable name, 'secs' into the console, I get undefined, even though it's defined as a parameter in the function called by my setTimeout. I don't know what I'm doing wrong. Can anyone help, please?
My full code is at https://codepen.io/Rburrage/pen/qBEjXmx;
Here's the JavaScript snippet:
function startTimer(secs, elem) {
t = $(elem);
t.innerHTML = "00:" + secs;
if(secs<0) {
clearTimeout(countDown);
}
secs--;
//recurring function
countDown = setTimeout('startTimer('+secs+',"'+elem+'")', 1000);
}
Add a condition to call recursive function like below.
if (secs < 0) {
secs = secsInput;
}
//recurring function
countDown = setTimeout('startTimer('+secs+',"'+elem+'")', 1000);
For a countdown timer, I would recommend using setInterval and clearInterval instead. setInterval will repeatedly run the callback function for you. It might look like this:
let countdown;
function startTimer(secs, elem) {
countdown = setInterval(function(){
t = $(elem);
t.innerHTML = "00:" + secs;
secs--
if (secs < 0) {
clearInterval(countdown);
}
}, 1000);
}
By the time you call clearTimeout(countDown), countDown refers to the previous timeout, that already timed out. It will not stop the one yet to start. You could just not re set the timeout, like
if(!/*finished*/) setTimeout(startTimer, 1000, secs, elem);
In your case, it's more convenient to use setInterval and clearInterval.
To keep the setTimeout and clearTimeout functions, you should add return in the if statement.
function startTimer(secs, elem) {
t = $(elem);
t.innerHTML = "00:" + secs;
if(secs<0) {
clearTimeout(countDown);
return;
}
secs--;
//recurring function
countDown = setTimeout('startTimer('+secs+',"'+elem+'")', 1000);
}
So there are 4 events in my opinion that will have to be addressed by the timer:
The quiz starts
The quiz ends
The timer runs out
The player answers a question
This can be solved by a function returning an object with some options.
The createTimer can be used to set the parameters for the timer.
Point 1. would be timer.start() --> will start a timer with the parameters
Point 3. can be addressed with the callback that will be called if the timer runs out --> createTimer(5,'display', ()=>{ // your code goes here })
Point 2. can be achieved with --> timer.stop()
Point 4. is needed when the timer needs to be reset without running out timer.reset()
Further on the interval is not in the global scope so you could have multiple timers with different settings and they wouldn't interfere with each other
// function for creating the timer
function createTimer(seconds, cssSelector, callbackOnTimeout) {
// interval the timer is running
let interval;
// the html node where innerText will be set
const display = document.getElementById(cssSelector)
// original seconds passt to createTimer needed for restart
const initSec = seconds
// starting or continuing the interval
function start() {
// setting interval to the active interval
interval = setInterval(() => {
display.innerText = `00:${seconds}`;
--seconds;
if (seconds < 0) {
// calling restart and callback to restart
callbackOnTimeout()
restart()
}
}, 1000);
}
// just stopping but not resetting so calling start will continue the timer
// player takes a break
function stop(){
clearInterval(interval)
}
// opted for a restart and not only a reset since it seemed more appropriate for your problem
function restart(){
clearInterval(interval)
seconds = initSec
start()
}
// returning the object with the functions
return {
start: start,
stop: stop,
restart: restart
}
}
// example for creating a timer
const timer1 = createTimer(5,'display',()=>{
console.log(`you where to slow ohhh...`)
})
// calling the timer
timer1.start()

Using setInterval to Save Form Every 60 Seconds Not Working

I'm working on some code that'd I'd like to have each loop run every 60 seconds, but currently each loop runs immediately. The purpose of the code it to see if a form has changed, and if it has save the form. Do I have setInterval setup incorrectly?
function saveHelper(formId) {
for(var i = 0; i < 4; i++) {
save(formId);
}
}
function save(formId) {
console.log('might save');
var changed = formChanges(formId);
var intId = setInterval(stall, 60000);
if(changed.length > 0) {
console.log('would save');
//document.getElementById(formId).submit();
}
clearInterval(intId);
}
function stall() {
return true;
}
You are treating interval as some sort of synchronous sleep method, which is not the case. The change code should be inside of the setInterval, it should not live after the interval.
var intId = setInterval(function () {
if(changed.length > 0) {
console.log('would save');
//document.getElementById(formId).submit();
}
}, 60000);
setInterval doesn't pause your code, it just schedules some code to be run some time in the future. For example, when you do this:
var intId = setInterval(stall, 60000);
That says "every 60000 milliseconds, run the function stall". As soon as this line of code completes, it will immediately run your next line of code, do the saving, then clear the interval. Clearing the interval cancels it, so now nothing will happen in 60000 milliseconds.
Instead, you'll want to do something like this:
function saveHelper(formId) {
let count = 0;
const intId = setInterval(function () {
if(changed.length > 0) {
console.log('would save');
//document.getElementById(formId).submit();
}
count++;
if (count === 4) {
clearInterval(intId);
}
}, 60000);
}
Every 60000 milliseconds, the inner function will run, and do the saving. After saving, it checks how many times we've done this, and once it reaches 4, it clears the interval to stop it from happening any more.

Resetting Timer Errors - Javascript

I am trying to create a one-minute timer that runs twice. During the first run, the timer will countdown to 0. Then, the timer will reset to one minute and start the countdown again. To create this timer, I am using Javascript, which is a programming language that I am not particularly skilled at.
The timer works when I use it just once. However, when I try to use it in a for loop, create a recursive function, or even run it twice in a row by calling the function twice, the countdown is not consecutive. For example, it will display 4, then 2, then -1, even though the countdown is supposed to be consecutive and end at 0.
I have searched on StackExchange to find similar questions. I followed the answer to this question, but the countdown displayed NaN instead of the proper numbers. I have also read through a number of other questions, but their answers have not worked for me.
My Javascript code is:
window.onload = function() {
var seconds = 60;
function timeout() {
setTimeout(function () {
seconds--;
document.getElementById("time").innerHTML = String(seconds)
if (seconds > 0){
timeout();
}
}, 1000);
}
timeout()
seconds = 60;
timeout()
};
document.getElementById("time") refers to an empty paragraph with time as its ID.
Thank you in advance.
Your code is running the function twice at the same time. Try this, setting a separate counter, and stopping the timer once the count has reached a set number:
window.onload = function() {
var seconds = 60;
var count = 1;
function timeout() {
setTimeout(function() {
seconds--;
document.getElementById("time").innerHTML = String(seconds)
if (seconds > 0) {
timeout();
} else {
seconds = 60;
count++;
if (count <= 2) {
timeout();
}
}
}, 1000);
}
timeout();
};
<p id="time"> </p>

javascript timer counting very fast as time passes

I want a counter which reset in specific interval of time. I wrote this code. When I refresh the page it is executing perfectly. But as time passes the timer goes really fast, skipping seconds. Any idea why this is happening.
function countdown_new() {
window.setInterval(function () {
var timeCounter = $("b[id=show-time]").html();
var updateTime = eval(timeCounter) - eval(1);
$("b[id=show-time]").html(updateTime);
if (updateTime == 0) {
//window.location = ("ajax_chart.php");
$("b[id=show-time]").html(5);
clearInterval(countdown_new());
// countdown_new();
//my_ajax();
}
}, 1000);
}
window.setInterval(function () {
countdown_new();
}, 5000)
HTML
Coundown in 5 seconds
The issue is because you are not clearing the previous timer before starting a new one, so you start a new one for each iteration. To clear the timer you should save a reference to it, and pass that to clearInterval, not a function reference.
Also, note that your pattern of using multiple intervals for different operations can lead to overlap (where two intervals are acting at the same time and cause odd behaviour). Instead, use setTimeout for the initial 5 second delay and then chain further calls to stop this overlap.
Try this:
var timer;
function countdown_new() {
timer = setInterval(function () {
var $showTime = $("#show-time")
var updateTime = parseInt($showTime.text(), 10) - 1;
$showTime.html(updateTime);
if (updateTime == 0) {
$showTime.html('5');
clearInterval(timer);
setTimeout(countdown_new, 5000);
}
}, 1000);
}
setTimeout(countdown_new, 5000);
Example fiddle
Note that you should use the # selector to select an element by its id attribute, and you should never use eval - especially not for type coercion. To convert a value to an integer use parseInt().
You are calling window.setInterval(), which schedules a function call to countdown_new() ever 5 seconds without stop.
Compounding the problem, you are calling countdown_new() again inside your clear interval.
You need to call setInterval just once to continuously execute a function every 5 seconds.
If you want to cancel an interval timer, you need do to this:
var intervalObj = setInterval(function() { ... }, 5000);
clearInterval(intervalObj);
Yes clearinterval does the trick.
function countdown_new(){
t = window.setInterval(function() {
var timeCounter = $("b[id=show-time]").html();
var updateTime = eval(timeCounter)- eval(1);
$("b[id=show-time]").html(updateTime);
if(updateTime == 0){
//window.location = ("ajax_chart.php");
$("b[id=show-time]").html(5);
clearInterval(t);
// countdown_new();
my_ajax();
}
}, 1000);
}

Categories