Im wondering what is wrong with this for loop here. I'm trying to make a Pomodoro Study Timer, a study technique that suggests that you break down studying into 25-minute chunks that are followed by 3-5 minute breaks. here I have 2 timers that run in sequence, one after the other. When the first timer reaches zero, the second one starts. For now, i have timers set to 5 seconds and 3 seconds respectively in order to make testing quicker. It all works fine until I put the whole thing into a for loop which then brings some unexpected behaviour. I want to loop the entire function based on user input which informs the code on how many times to loop the counters(this isnt setup yet).
The timers are started by pressing a button on an html page. The button executes the pomo() function at the bottom, which contains a loop that should loop the start() function.
PS, I'm a total ultra noob so apologies if this is just terrible code, I'm really new to this :)
var time25 = 5;
var time5 = 3;
var timeElapsed25 = 0;
var timeElapsed5 = 0; // initializes time elapsed to zero
var time = document.getElementsByClassName("header"); //links to html
time[0].innerHTML = time25; // sets output to html
function convertToMin(s) {
mins = Math.floor(s / 60);
let minsStr = mins.toString();
if (minsStr.length === 1) {
mins = '0' + mins;
}
sec = s % 60;
let secStr = sec.toString();
if (secStr.length === 1) {
sec = '0' + sec;
}
return mins + ':' + sec;
}
function start() {
var timer25 = setInterval(counter25, 1000);
console.log("timer1");
function counter25() {
timeElapsed25++
time[0].innerHTML = convertToMin(time25 - timeElapsed25);
if (timeElapsed25 === time25) {
console.log("timer2")
clearInterval(timer25);
timeElapsed25 = 0;
var timer5 = setInterval(counter5, 1000);
function counter5() { //Counter For 5 minute break
timeElapsed5++;
time[0].innerHTML = convertToMin(time5 - timeElapsed5);
if (timeElapsed5 === time5) {
clearInterval(timer5);
timeElapsed5 = 0;
}
}
}
}
}
function pomo() {
for (j = 0; j < 3; j++) {
start();
}
}
You shouldn't call start() in a loop. setInterval() doesn't wait for the the countdown to complete, it returns immediately, so you're starting all 3 timers at the same time.
What you should do is call start() again when both timers complete. To put a limit on the number of repetitions, use a count parameter, and decrement it each time you call again.
var time25 = 5;
var time5 = 3;
var timeElapsed25 = 0;
var timeElapsed5 = 0; // initializes time elapsed to zero
var time = document.getElementsByClassName("header"); //links to html
time[0].innerHTML = time25; // sets output to html
function pomo() {
start(3);
}
function start(count) {
if (count == 0) { // reached the limit
return;
}
var timer25 = setInterval(counter25, 1000);
console.log("timer1");
function counter25() {
timeElapsed25++
time[0].innerHTML = convertToMin(time25 - timeElapsed25);
if (timeElapsed25 === time25) {
console.log("timer2")
clearInterval(timer25);
timeElapsed25 = 0;
var timer5 = setInterval(counter5, 1000);
function counter5() { //Counter For 5 minute break
timeElapsed5++;
time[0].innerHTML = convertToMin(time5 - timeElapsed5);
if (timeElapsed5 === time5) {
clearInterval(timer5);
timeElapsed5 = 0;
start(count - 1); // Start the next full iteration
}
}
}
}
}
function convertToMin(s) {
mins = Math.floor(s / 60);
let minsStr = mins.toString();
if (minsStr.length === 1) {
mins = '0' + mins;
}
sec = s % 60;
let secStr = sec.toString();
if (secStr.length === 1) {
sec = '0' + sec;
}
return mins + ':' + sec;
}
Related
The goal is to make the button turn off if pressed 10 times in less than 1 minute but keep counting if not pressed 10 times in 1 minute? so only disables when pressed 10 times in less than 1 minute. You can click nine times in a minute but nothing happens but the tenth time the button turns off, but if the minute has passed you can keep clicking but always for a maximum ten times and the counter does not reset but continues to increase in number.
let accCounter = 0;
let totalCount = 0;
document.getElementById('totalCounter').innerText = totalCount;
document.getElementById('clap').onclick = function() {
const clap = document.getElementById('clap');
const clickCounter = document.getElementById("clicker");
upClickCounter();
function upClickCounter() {
const clickCounter = document.getElementById("clicker");
const totalClickCounter = document.getElementById('totalCounter');
accCounter++;
clickCounter.children[0].innerText = '+' + accCounter;
totalClickCounter.innerText = totalCount + accCounter;
}
}
var endCount = 5; // 5 for testing - this would be 10
var interval = 5000 // 5s for testing, 1 min= 1000 * 60;
var clicks = [];
var totalClicks = 0;
$("#btn").click(function() {
clicks.push(Date.now());
totalClicks++;
checkIt();
})
function checkIt() {
//if (clicks.length < endCount) return;
while (clicks.length && (Date.now() - clicks[0]) > interval) {
console.log("removing: " + ((Date.now() - clicks[0]) / 1000))
clicks.shift();
}
if (clicks.length < endCount) return;
$("#btn")[0].disabled = true;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="clap" class="clap-container"></button>
<div id="totalCounter" class="total-counter"></div>
<div id="clicker" class="click-counter"></div>
I'm working on a small project between school projects, and I'm having a problem with a DOM element where I use innerText to go from a clock to text, and then after 5s, I want to empty that text. Everything works but the text flicker after a while.
// Break Time // Break Time // Break Time // Break Time
let inputTime = document.getElementById('breakTimeInput');
document.querySelector('#cta-paus').addEventListener('click', function () {
if (inputTime.value === '') {
swal('Break Time', 'You forgot to set break time!', 'warning');
return;
}
let breakTime = inputTime.value * 60;
setInterval(updateCountdown, 1000);
function updateCountdown() {
let minutes = Math.floor(breakTime / 60);
let seconds = breakTime % 60;
// Adds zero infront of secunds
seconds = seconds < 10 ? '0' + seconds : seconds;
// Out puts the time
document.querySelector(
'#MyClockDisplayDown'
).innerText = `${minutes}:${seconds}`;
breakTime--;
// Removes zero when minutes are done
if (minutes == 0) {
document.querySelector('#MyClockDisplayDown').innerText = `${seconds}`;
}
// If the count down is finished, write some text
if (breakTime < 0) {
document.querySelector('#MyClockDisplayDown').innerText = 'On Air';
inputTime.value = '';
setTimeout(() => {
document.querySelector('#MyClockDisplayDown').innerText = '';
}, 2000);
}
}
});
You would need to call clearInterval when finished: the updateCountdown function is still called every second
Something like this?
...
document.querySelector('#cta-paus').addEventListener('click', function () {
...
var intervalId = setInterval(updateCountdown, 1000);
function updateCountdown() {
...
// If the count down is finished, write some text
if (breakTime < 0) {
...
clearInterval(intervalId); // <=== Add this
setTimeout(() => {
document.querySelector('#MyClockDisplayDown').innerText = '';
}, 2000);
}
}
I'm trying to create a 'Pomodoro' timer that takes user input and creates a timer based on how long somebody wants to use it for (for no reason other than to learn and use it for myself).
I'm finding that my for loops aren't behaving as I'd expect them to and when you look at the timer itself, it is counting down every second, however the timer itself actually reduces by 6 seconds for every one second counted.
I also can't seem to get the timer to move on to the next bit once it hits zero.
I did originally have breaks in the function so that it would move from the current time to the rest time but that didn't seem to do the trick.
In terms of the 6 seconds problem, I'm not even sure where to begin with that.
// set up a counter for how many times you want to set the pomodoro for - users will input how many cycles they want the program to go through.
const pomodoroQuestion = prompt("How many times would you like to use the pomodoro (1 Pomodoro = 3x 25 minute working burst, 2x 5 minute breaks and 1x 15 minute break)");
const pomodoroLength = parseInt(pomodoroQuestion);
for (let i = 0; i < pomodoroLength; i++) {
function startTimer() {
const currentTime = document.getElementById('pomodoroClock').innerHTML;
const timeArray = currentTime.split(/[:]+/);
let minutes = timeArray[0];
let seconds = secondsTimer((timeArray[1] - 1));
if (seconds === 59) {
minutes = minutes - 1;
}
if (minutes < 0) {
alert("Time's up");
}
document.getElementById('pomodoroClock').innerHTML = `${minutes}:${seconds}`;
setTimeout(startTimer, 1000); // Make the function countdown each second
}
// cycle through the seconds
function secondsTimer(sec) {
if (sec < 10 && sec >= 0) {
sec = `${0}${sec}`;
}
if (sec < 0) {
sec = 59;
}
return sec;
}
// the following loop will be what starts the actual pomodoro cycle.
for (let x = 0; x < 3; x++) {
// function starting a countdown timer for 25 minutes
document.getElementById('pomodoroClock').innerHTML = `${25}:${00}`;
startTimer();
if (x < 2) {
// this is where you're going to perform the function that'll allow for a 5 minute break
document.getElementById('pomodoroClock').innerHTML = `${05}:${00}`;
startTimer();
} else {
// this is where you're going to perform the function that'll allow for a 15 minute break
document.getElementById('pomodoroClock').innerHTML = `${15}:${00}`;
startTimer();
}
}
} // end pomodoroLength loop
<div id="pomodoroClock" class="timer"></div>
<script src="script/script.js"></script>
Where am I going wrong with this one? I feel like I'm just missing a few key pieces of understanding with projects like this, hence creating little practice projects to improve.
I think it's worthwhile to change your approach. What if you had a stand-alone countdown() function that displays minutes and seconds in a given target element, and notifies you when it's done?
That's easy to do with promises. You make a function that returns a new Promise, and you resolve() that promise when the time hits zero:
function countdown(minutes, seconds, targetElement) {
return new Promise(resolve => {
const tick = setInterval(function () {
// count down, display current time in targetElement
if (/* time's up */) {
// stop interval, call resolve()
}
}, 1000);
});
}
And since this function returns a promise, it becomes straightforward to chain multiple of those functions with async/await:
async function countdownSequence(...timers) {
for (let t of timers) {
await countdown(0, t, document.getElementById('target'));
}
alert('done!');
}
countdownSequence(5, 10, 5); // counts 5, 10, and 5 seconds, and then alerts 'done!'
Full implementation with a few extras. Note that for the sake of the example, instead of using your sequence 25, 5, 25, 5, 25, 15 for each round, I'm using 5, 2, 5, 2, 5, 3, and I'm using the seconds slot of the countdown function.
function countdown(minutes, seconds, targetElement) {
const pad = num => (num < 10 ? '0' : '') + num;
const display = () => targetElement.textContent = pad(minutes) + ':' + pad(seconds);
return new Promise(resolve => {
const tick = setInterval(function () {
seconds--;
if (seconds < 0) {
minutes--;
seconds = 59;
}
if (minutes < 0) {
clearInterval(tick);
resolve();
}
display();
}, 1000);
display();
});
}
async function pomodoro(numCycles, targetElement) {
targetElement.classList.add('active');
for (let i = 0; i < numCycles; i++) {
targetElement.classList.remove('work');
for (let minutes of [5, 2, 5, 2, 5, 3]) {
targetElement.classList.toggle('work');
await countdown(0, minutes, targetElement);
}
}
targetElement.classList.remove('active');
}
async function start() {
const cycles = parseInt(prompt("How many times would you like to use the pomodoro (1 Pomodoro = 3x 25 minute working burst, 2x 5 minute breaks and 1x 15 minute break)"), 10);
if (cycles > 0) {
await pomodoro(cycles, document.getElementById('pomodoroClock'));
alert("Finished!");
}
}
start();
#pomodoroClock {
display: none;
}
#pomodoroClock.active {
display: block;
color: blue;
}
#pomodoroClock.work {
color: green;
}
#pomodoroClock::after {
padding-left: 5px;
content: '(pause)';
}
#pomodoroClock.work::after {
padding-left: 5px;
content: '(work time)';
}
<div id="pomodoroClock"></div>
I was trying to implement a function, which is supposed to post measurement A every 5 sec for 10 times, and then post measurement B every 5 sec for a random amounts of time. And I want repeat this function forever as I was trying to implement a fake agent.
So I had the code:
let intervalId = null, repeat = 0;
while (true) {
intervalId = setInterval(() => {
if (repeat < 5) {
// post measurement A
repeat += 1;
}
else {
clearInterval(intervalId)
}
}, 1000);
repeat = 0;
intervalId = setInterval(() => {
if (repeat < Math.floor(Math.random() * 11)) {
// post measurement B
repeat += 1;
}
else {
clearInterval(intervalId)
}
}, 1000);
}
The two setInterval() function didn't happen consecutively as I expected, instead they happened at the same time. And the while (true) loop seems not behave as expected either. I'm just wondering is there any way to get around with this problem? Thanks.
You can create two function, one is doA() and one is doB().
Start with doA(), count the number of time //do A is called, when it reached 10, clearInterval and call doB().
In doB(), set the min and max time it should be called, then when it reached randTime clearInterval and doA()
function doA() {
let count = 0;
const a = setInterval(() => {
//do A
console.log('do A');
count += 1;
if (count === 10) {
clearInterval(a);
doB();
}
}, 5000/10);
}
function doB() {
// set your min and max for B
const minTime = 1;
const maxTime = 10;
const randTime = Math.floor(Math.random() * (maxTime - minTime + 1)) + minTime;
let count = 0;
const b = setInterval(() => {
// do B
console.log(randTime);
console.log('do B');
count += 1;
if (count === randTime) {
clearInterval(b);
doA();
}
}, 5000 / randTime);
}
doA();
Working on top of your code, first thing first, remove infinite while loop. It will run endlessly in synchronous fashion while setInterval is asynchronous. repeat value will be far ahead before you do repeat += 1.
Second, break them down in function so they have their own closure for intervalId and repeat value.
function intervalA () {
let intervalId = null
let repeat = 0
intervalId = setInterval(() => {
if (repeat < 5) {
console.log(new Date(), 'A')
// post measurement A
repeat += 1; // increment repeat in callback.
}
else {
clearInterval(intervalId); // done with interval, clear the interval
intervalB(); // and start interval B
}
}, 1000)
}
function intervalB () {
let repeat = 0
let randomEnd = Math.floor(Math.random() * 11) // calculate when it should end.
let intervalId = setInterval(() => {
if (repeat < randomEnd) {
console.log(new Date(), 'B will finish in', randomEnd, 'times')
repeat += 1
}
else {
clearInterval(intervalId) // clear the interval once done
}
}, 1000)
}
intervalA(); //start with interval A
Currently, the intervals are being set at once, synchronously, at the start of your script and during every while thereafter. It would probably be clearer if you only a single interval, with a variable that indicated which measurement to run, and change that variable every random-nth iteration:
const getRandomRepeats = () => Math.ceil(Math.random() * 11)
let repeatsRemaining = getRandomRepeats();;
let measureA = true;
setInterval(() => {
repeatsRemaining--;
if (repeatsRemaining === 0) {
repeatsRemaining = getRandomRepeats();
measureA = !measureA;
}
console.log('repeats remaining: ' + repeatsRemaining);
if (measureA) {
console.log('posting a');
} else {
console.log('posting b');
}
}, 1000);
So I made a simple timer in a format like this: MM:SS.MS
You can view it here [removed]
It works fine in chrome, IE, etc. but in Firefox the seconds are like twice as long..
I took a look at some other stopwatches, but I don't quit understand them.
Whats the best way to do it ? Right now I have a 10ms interval which generates the timer.
The function looks like that, I hope it's understandable:
var state = false;
var msTimer = null;
var min = document.getElementById("min");
var sec = document.getElementById("sec");
var ms = document.getElementById("ms");
var minCount = 0;
var secCount = 0;
var msCount = 0;
document.onkeydown = function timer(e) {
if (!e) { e = window.event; }
if (e.keyCode == "32") {
if (state == false) {
state = true;
min.innerHTML = "00";
sec.innerHTML = "00";
ms.innerHTML = "00";
msTimer = window.setInterval(function() {
if (msCount == 99) {
msCount = 0;
secCount++;
// Minutes
if (secCount == 60) {
secCount = 0;
minCount++;
if (minCount <= 9)
{ min.innerHTML = "0" + minCount; }
else
{ min.innerHTML = minCount; }
}
// Seconds
if (secCount <= 9)
{ sec.innerHTML = "0" + secCount; }
else
{ sec.innerHTML = secCount; }
} else { msCount++; }
// Miliseconds
if (msCount <= 9)
{ ms.innerHTML = "0" + msCount; }
else
{ ms.innerHTML = msCount; }
// 1 Hour
if (minCount == 60) {
clearInterval(msTimer);
min.innerHTML = "N0";
sec.innerHTML = "00";
ms.innerHTML = "0B";
state = false;
minCount = 0;
secCount = 0;
msCount = 0;
}
}, 10);
} else if (state == true) {
state = false;
clearInterval(msTimer);
minCount = 0;
secCount = 0;
msCount = 0;
}
}
Thanks for any advices :)
Edit:
And btw, it's much smoother in firefox, if I remove all styles from the timer.
But that can't be the solution..
You shouldn't be relying on the interval of the timer being exactly 10ms. It would be better if you viewed each timer tick as a request to refresh the on-screen timer, but take the measurements from the system clock or something like that.
Then however slow or busy the machine (and JS implementation) is, you'll always see an accurate timer1 - just one which updates less often.
1 This will only be as accurate as the system clock, of course. If Javascript has access to a high-performance timer like .NET's Stopwatch class or Java's System.nanoTime() method, that would be better to use.
Timing in Javascript is not guaranteed. A 10ms interval will only ever be approximately 10ms, most likely it will be delayed a tiny bit each time. The correct way to do a timer in Javascript is to save a starting timestamp upon starting the timer, then each 10ms or so calculate the difference between the starting timestamp and the current time and update an element on the page with the formatted value.