I’m working on the Pomodoro Clock project and have made a lot of progress so far.
The clock is functioning correctly for all of the key requirements (you can set the session/break length, pause execution and come back to where you left off, reset, and it gives you a notification sound when the session is over).
But there’s a bug in my program.
When the start/stop button is pressed several times in a quick succession, the countdown function is triggering twice (causing the clock to countdown two seconds for every second passed). You can replicate the bug with the demo here – just click the start/stop button a few times quickly to see what I mean.
I tried wrapping the entire function in an IIFE with an if
statement checking whether the function should execute (based on a bool variable that confirms if the timer should be active), but that didn’t change the functionality.
Any advice appriciated! I’ve been sitting looking at the code for ages now and can’t see any way to make it work.
The live demo is here.
The complete code is here.
Below is the countdown timer function on its own.
// Methods for the countdown timer.
var timerController = {
timerActiveSwitch: function() {
model.timerActive = !model.timerActive;
if (elements.countdownToggle.innerHTML === "<p>Start</p>") {
elements.countdownToggle.className = "stopBtn";
elements.countdownToggle.innerHTML = "<p>Stop</p>";
} else {
elements.countdownToggle.className = "startBtn";
elements.countdownToggle.innerHTML = "<p>Start</p>";
}
},
reset: function() {
model.timerActive = false;
model.countdownStep = "session";
model.breakInt = 5;
model.sessionInt = 25;
userToggle.timeNull();
elements.timerTitle.innerHTML = "Session";
elements.breakIntDOM.innerHTML = model.breakInt;
elements.sessionIntDOM.innerHTML = model.sessionInt;
elements.timer.innerHTML = model.sessionInt;
elements.countdownToggle.className = "startBtn";
elements.countdownToggle.innerHTML = "<p>Start</p>";
},
stepChange: function() {
model.countdownStep = "break";
elements.timerTitle.innerHTML = "Break";
var audio = new Audio();
audio.src = "assets/notification.mp3"
audio.play();
},
countdownTimerActivator: function() {
this.timerActiveSwitch();
if (model.countdownStep === "session") {
if (model.mins === null || model.seconds === null) {
model.mins = model.sessionInt;
model.seconds = 0;
}
} else if (model.countdownStep === "break") {
if (model.mins === null || model.seconds === null) {
model.mins = model.breakInt;
model.seconds = 0;
}
}
var timer = setInterval(timeUpdate, 1000);
function clearTimer() {
clearTimeout(timer);
}
// Does the actual countdown each second.
function timeUpdate() {
if (!model.timerActive) {
clearTimer();
} else if (model.mins === 0 && model.seconds === 0) {
clearTimer();
model.mins = null;
model.seconds = null;
} else if (model.seconds === 0) {
model.mins--;
model.seconds = 59;
} else {
model.seconds--;
}
if (model.countdownStep === "session") {
if (model.timerActive && model.mins !== null && model.seconds !== null) {
elements.timer.innerHTML = model.mins + ":" + model.seconds;
} else if (model.timerActive && model.mins === null && model.seconds === null) {
clearTimer();
timerController.stepChange();
timerController.timerActiveSwitch();
timerController.countdownTimerActivator();
}
} else if (model.countdownStep === "break") {
if (model.timerActive && model.mins !== null && model.seconds !== null) {
elements.timer.innerHTML = model.mins + ":" + model.seconds;
} else if (model.timerActive && model.mins === null && model.seconds === null) {
clearTimer();
timerController.reset();
return;
}
}
}
},
};
Update
I managed to resolve the issue. The code that fixed my problem:
function startTimer() {
clearInterval(timer);
timer = setInterval(timeUpdate, 1000);
}
startTimer();
I’m actually a little ashamed it took me this long to see the problem.
Having the timer = setInterval(timeUpdate, 1000);
variable just inside the main countdownTimerActivator
was the issue. If the start/stop button was clicked a few times quickly, it was creating several intervals (causing a faster countdown). The code works by clearing any existing intervals every time an interval is created.
It seems to have been a scoping issue (that’s why the if statement checks reccomended weren’t working – if statements don’t have their own scope so the timer
variable was still scoped to the countdownTimerActivator
function).