How to clear JS timers - javascript

I've got a user which i get from database. For each user is possible to reset his password. But it's possible only one time for three minutes. If password was reseted less than three minutes, I show timer with JS, which show how much time left for next reset and reset button is disabled. When countdown is finished, button become availiable and displayed inscription "Password reset avaliable". Because the each user has his own timer, when I select another user, previous timer must be stopped with reset() function, and must be started other timer. It's just the only one timer must be in one time.
Timer:
var timerCount = 0;
function startTimer(minute, second) {
timerCount++;
start(minute, second);
}
function start(minute, second) {
disableButton('resetPasswordButton');
var m = minute;
var s = second;
if (timerCount == 0) {
document.getElementById('expiredTimeOutputText').innerHTML = "Password reset avaliable";
m = 0;
s = 0;
enableButton('resetPasswordButton');
return ;
}
if (s == 0) {
if (m == 0) {
reset();
document.getElementById('expiredTimeOutputText').innerHTML = "Password reset avaliable!";
enableButton('resetPasswordButton');
return ;
}
m--;
s = 59;
} else
s--;
document.getElementById('expiredTimeOutputText').innerHTML = m + ":" + s;
setTimeout(function () {
start(m, s);
}, 1000);
}
function reset() {
if (timerCount > 0) {
timerCount = 0;
}
}
function enableButton(id){
document.getElementById(id).disabled = false;
}
function disableButton(id){
document.getElementById(id).disabled = true;
}
method to start timer on button click
public void changePassword() {
RequestContext requestContext = RequestContext.getCurrentInstance();
requestContext.execute("startTimer(\"0\", \"40\")");
Date tmpDate = new Date();
Long diff = tmpDate.getTime();
mainDataBean.setResetTimer(applicantsTableSelectedRow.get("ID"), diff.toString());
}
method to start timer on user change
public void checkTimerForNewUser() {
RequestContext requestContext = RequestContext.getCurrentInstance();
Date tmpDate = new Date();
Long currentTime = tmpDate.getTime();
requestContext.execute("reset()");
if (applicantsTableSelectedRow != null) {
if (!mainDataBean.getResetTimer(applicantsTableSelectedRow.get("ID")).equals("noTimer")) {
Long applicantTimerTime = Long.parseLong(mainDataBean.getResetTimer(applicantsTableSelectedRow.get("ID")));
if (currentTime - applicantTimerTime > timerValue) {
mainDataBean.deleteResetTimer(applicantsTableSelectedRow.get("ID"));
}
else {
expiredTimeMinute = (timerValue - (currentTime - applicantTimerTime)) / 60000;
expiredTimeSecond = (timerValue - (currentTime - applicantTimerTime)) / 1000 - expiredTimeMinute * 60;
requestContext.execute("startTimer(\"" + expiredTimeMinute + "\", \"" + expiredTimeSecond + "\")");
}
}
}
}
If consistently reset user password, one after another, everything works fine. But if I consistently reset password for 5 users, and after that I return to first user with reseted password, if time isn't expired, I've got all 5 timers, which overlap one another, display 5 different times 5 time in second. But in theory they must stop because of reset function. How does it possible to make the only one timer exist? Do I stop functions wrong?
Example with clearTimeout:
function start(minute, second) {
disableButton('resetPasswordButton');
var m = minute;
var s = second;
if (timerCount == 0) {
document.getElementById('expiredTimeOutputText').innerHTML = "Reset Button Available!";
clearTimeout(timeout);
enableButton('resetPasswordButton');
return ;
}
if (s == 0) {
if (m == 0) {
reset();
clearTimeout(timeout);
document.getElementById('expiredTimeOutputText').innerHTML = "Reset Button Available!";
enableButton('resetPasswordButton');
return ;
}
m--;
s = 59;
} else
s--;
document.getElementById('expiredTimeOutputText').innerHTML = m + ":" + s;
timeout = setTimeout(function () {
start(m, s);
}, 1000);
}

You can clear timers with:
clearTimeout for setTimeout
clearInterval for setInterval
Example:
// will *never* run since we clear it below, immediately after we
// create it.
var timeout = setTimeout(() => {
console.log('foo')
}, 1000)
clearTimeout(timeout)
// will run only *once* since we clear it when it's called
var interval = setInterval(() => {
console.log('bar')
clearInterval(interval)
}, 1000)

Related

How to keep running my Countdown after a refresh?

I'm learning JS and I'm stuck...
I want to create a countdown (it's done, my countdown is working) who keep running when the page is reloaded.
I used the sessionStorage to "save" the countdown value and also to check if a sessionStorage exists when the page is loaded.
The problem is, I don't know how keep running the countdown with values saved in the sessionStorage.
Could you please help me?
class Timer {
constructor(secondes, minutes) {
this.secondes = secondes;
this.minutes = minutes;
this.button = document.getElementById("button");
this.counter = document.getElementById("counter");
this.storageCheck();
}
countdown(minutes) {
var seconds = this.secondes;
var mins = this.minutes;
var myCounter = this.counter;
function tick() {
var current_minutes = mins-1;
seconds--;
myCounter.innerHTML = current_minutes + ":" + (seconds < 10 ? "0" : "") + seconds;
var duration = sessionStorage.setItem("timer", myCounter.innerHTML);
if( seconds > 0 ) {
setTimeout(tick, 1000);
} else {
if(mins > 1){
countdown(mins-1);
}
}
}
tick();
}
buttonClick() {
button.addEventListener("click", () => {
this.countdown(this.minutes);
})
}
storageCheck() {
if (sessionStorage.getItem("timer")) {
// keep the countdown running
}
}
}
let newTimer = new Timer(60, 20);
newTimer.buttonClick();
<!DOCTYPE html>
<html>
<head>
<title>Test Countdown</title>
</head>
<body>
<div id="counter"></div>
<button id="button">Run</button>
<script type="text/javascript" src="countdown.js"></script>
</body>
</html>
In constructor before initializing secondes and minutes check if they are in the storage.If they dont exists then only set the this.secondes = secondes and this.minutes = minutes;
constructor(secondes, minutes) {
this.button = document.getElementById("button");
this.counter = document.getElementById("counter");
if(!this.storageCheck()){ //check if seconds are mins are stored in storage
this.secondes = secondes; //if not set mins and sec to value passed in constructor
this.minutes = minutes;
}
else{
this.countdown(this.minutes);
}
}
In storage check function , check if the values are there, if there get the values and set to this.secondes and this.minutes and return true else return false
storageCheck() {
//if both mins and secs exists
if (sessionStorage.getItem("mins") &&sessionStorage.getItem("secs")) {
// keep the countdown running
this.minutes=parseInt(sessionStorage.getItem("mins"));//get min
this.secondes=parseInt(sessionStorage.getItem("secs"));//get secs
return true;
}
else
return false;
}
and in countdown funciton save the values to storage
sessionStorage.setItem("mins",vm.minutes);//set current min
sessionStorage.setItem("secs",vm.secondes);//set current sec
Try running this here :https://jsbin.com/bavexigute/1/edit?html,js,console,output
class Timer {
constructor(secondes, minutes) {
this.button = document.getElementById("button");
this.counter = document.getElementById("counter");
if(!this.storageCheck()){ //check if seconds are mins are stored in storage
this.secondes = secondes; //if not set mins and sec to value passed in constructor
this.minutes = minutes;
}
else{
this.countdown();
}
}
countdown() {
debugger;
var vm=this;
if(!(this.minutes-1<0))
this.minutes--;
let tick= function(){
vm.secondes--
if(vm.secondes==0){
vm.secondes=59;
vm.minutes--;
}
vm.counter.innerHTML = vm.minutes + ":" + (vm.secondes < 10 ? "0" : "") + vm.secondes;
if(vm.minutes == 0 && vm.secondes-1==0){
vm.secondes--;
vm.counter.innerHTML = vm.minutes + ":" + vm.secondes-1;
}
else{
setTimeout(tick,1000);
}
sessionStorage.setItem("mins",vm.minutes);//set current min
sessionStorage.setItem("secs", vm.secondes);//set current sec
}
setTimeout(tick,1000);
}
buttonClick() {
button.addEventListener("click", () => {
this.countdown();
})
}
storageCheck() {
//if both mins and secs exists
if (sessionStorage.getItem("mins") && sessionStorage.getItem("secs")) {
// keep the countdown running
this.minutes=parseInt(sessionStorage.getItem("mins"));//get min
this.secondes=parseInt(sessionStorage.getItem("secs"));//get secs
return true;
}
else
return false;
}
}
let newTimer = new Timer(60, 20);
newTimer.buttonClick();
You could use your storage check function to override the minutes and seconds arguments if it exists.
constructor(mins, secs) {
this.mins = mins
this.secs = secs
this.checkStorage = this.checkStorage.bind(this)
this.checkStorage(mins, secs)
}
checkStorage(mins, secs) {
if(window.storage) { // or whatever
this.secs = window.storage.secs
this.mins = window.storage.mins
}
}
Something along those lines. Basically just have the setStorage function change the values that have been set in the constructor.
You can do a simple trick by doing this
window.onload = function() {
let minutes = sessionStorage.getItem("minutes")
let seconds = sessionStorage.getItem("seconds")
let newTimer = new Timer(seconds, minutes);
};
And in sessionStorage instead of storing the whole innerHtml store minutes and seconds hopefully it will solve your problem

timer on sessionStorage stop on refresh

I am doing a timer that I store in a sessionStorage but if a refresh the page the timer stop! some help please
function time (secondes) {
const temps = Date.now();
const apres = temps + secondes * 1000;
const timer = setInterval(() => {
const secondsLeft = Math.round((apres - Date.now()) / 1000)
if (secondsLeft < 0) {
clearInterval(timer);
sessionStorage.clear();
if (sessionStorage.getItem('Timer') === null) {
$(".reservation").css("display", "none");
}
return;
}
sessionStorage.setItem("Timer", secondsLeft)
}, 1000);
}
the getItem("timer") here is just to check if the timer is over, I am using this Timer item in another method
thx
SessionStorage persist even if the page reloads. Read this:https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage
What you should do is, after you refresh the page, get the value of Timer again from SessionStorage and then again call the method.
function time (secondes) {
const temps = Date.now();
const apres = temps + secondes * 1000;
const timer = setInterval(() => {
const secondsLeft = Math.round((apres - Date.now()) / 1000)
if (secondsLeft <= 0) {
clearInterval(timer);
sessionStorage.clear();
//you dont need below if condition as secondsLeft is already 0. Commented if condition.
//if (sessionStorage.getItem('Timer') === null) {
$(".reservation").css("display", "none");
//}
return;
}
else{
//update the div content and show the current timer value in the div or in the span inside the div. (write a separate function for this).
}
sessionStorage.setItem("Timer", secondsLeft)
}, 1000);
}
//Method to call after page load:
function startTimerOnPageLoad(){
var x = parseInt( sessionStorage.getItem('Timer'));
//put a check, Timer is present in sessionStorage and x is not undefined. I've not added it here purposely.
time(x);
}

Stop and run again timer via pure javascript

I want to make a simple countdown timer which can be set by + or - and also it can be stopped and run by clicking on itself.
My problem is when it is stopped and then runs it shows NAN for the first number.
I suppose it is because of setTimer function but I don't know how to fix that.Here is my code:
var x = document.getElementsByClassName('session');
var seconds = 60;
var session;
var t;
var on = true;
var minutes = 1;
for (var i = 0; i < x.length; i++) {
x[i].innerHTML = minutes;
}
function increase() {
minutes++;
for (var i = 0; i < x.length; i++) {
x[i].innerHTML = minutes;
}
}
function decrease() {
minutes--;
for (var i = 0; i < x.length; i++) {
if (x[i].innerHTML > 0) {
x[i].innerHTML = minutes;
}
}
}
function setSession() {
session = x[1].innerHTML - 1;
}
function timer() {
if (seconds > 0) {
seconds--;
if (seconds == 0 && session > 0) {
session--;
seconds = 60;
}
}
x[1].innerHTML = session + ':' + seconds;
}
function stoptimer() {
clearInterval(t);
}
function general() {
if (on) {
on = false;
t = setInterval(timer, 100);
} else {
on = true;
stoptimer();
}
}
<div class='session'></div>
<div id='increase' onclick='decrease()'>-</div>
<div id='increase' onclick='increase()'>+</div>
<div class='session' onclick='setSession();general()'></div>
You shouldn't be setting session from the html entity. Basically this creates issues with parsing and could potentially break your code. Also, you subtract one every time you get this value, throwing a spanner in the works.
I re-shuffled your code a bit and added some notes, take a look:
var x = document.getElementsByClassName('session');
var seconds = 0;
var session;
var timer; // give this a useful name
var timerRunning = false; // give this a useful name
var minutes = 1;
function updateMinutes(newMinutes){
if (timerRunning){
return; // do not allow updating of countdown while count down is running
}
if(newMinutes !== undefined){ // allow this function to be called without any parameters
minutes = newMinutes;
}
if(minutes < 1){
minutes = 1; //set your minimum allowed value
}
if(minutes > 99999){
minutes = 99999; //could also have some sort of maximum;
}
for (var i = 0; i < x.length; i++) {
x[i].innerHTML = minutes;
}
session = minutes; // now this can only be set while timer is not running, so no need to get it from the html
//also, i would let this start at the exact same value as minutes, and have seconds start at zero
}
updateMinutes(); // call this now to initialise the display
function increase() {
updateMinutes(minutes + 1);
}
function decrease() {
updateMinutes(minutes - 1);
}
function timer_tick() {
if (seconds > 0) {
seconds--;
if (seconds == -1 && session > 0) { //because of where you've positioned your logic, this should check for negative one, no zero, otherwise you'll never display a zero seconds value
session--;
seconds = 59; //when a timer clocks over it goes to 59
}
}
if (session > 0 || seconds > 0){
x[1].innerHTML = session + ':' + seconds;
}
else{// you need to detect the ending
x[1].innerHTML = "Finished!!";
}
}
function timer_stop() {
clearInterval(timer);
}
function timer_start(){
timer = setInterval(timer_tick, 1000);
}
function timer_toggle() { //give these timer functions consistent names
if (!timerRunning) {
timer_start();
} else {
timer_stop();
}
timerRunning = !timerRunning; //just flip the boolean
}
You set
x[1].innerHTML = session + ':' + seconds;
and then try to calculate that as
session = x[1].innerHTML - 1;
You need to either put the session:seconds in another place or do
session = parseInt(x[1].innerHTML,10) - 1;
Ok, I would propose another approach. Use a class for your timer, like this:
function MyTimer(htmlEl) {
this.sec = 0;
this.min = 0;
this.elt = htmlEl;
}
MyTimer.prototype.set = function(m) {
this.min = m;
this.display();
var self = this;
this._dec = function() {
self.sec--;
if (self.sec < 0) {
if (self.min == 0) {
self.stop();
} else {
self.min -= 1;
self.sec = 59;
}
}
self.display();
}
}
MyTimer.prototype.display = function() {
this.elt.innerHTML = this.min + ":" + this.sec;
}
MyTimer.prototype.toggle = function() {
if (this.interval) {
this.stop();
this.interval = undefined;
} else this.start();
}
MyTimer.prototype.start = function() {
this.interval = setInterval(this._dec, 100);
};
MyTimer.prototype.stop = function() {
clearInterval(this.interval);
};
Then, you can use it like this:
window.onload = init;
var minutes, x, timer;
function init() {
x = document.getElementsByClassName('session');
timer = new MyTimer(x[1]);
minutes = 0;
}
function increase() {
minutes += 1;
x[0].innerHTML = minutes;
timer.set(minutes);
}
function decrease() {
minutes -= 1;
x[0].innerHTML = minutes;
timer.set(minutes);
}
function general() {
timer.toggle();
}
The only change in your html is to remove the setSession call:
<div id='timer' onclick='general()'></div>
This code is more clear. You encapsulate the logic and the min/sec in an object, which is reusable. Here is a working fiddle : https://jsfiddle.net/Shitsu/zs7osc59/.
The origin of your problem is in the code
session = x[1].innerHTML - 1;
Let's re-visit the purpose of keeping of the variable 'session'. I guess it is to keep the value of the maximum value, the upper limit of the minutes, from where to start counting, right? On the other hand, the 'minutes' variable is to keep the temporary value of the minutes. What confused you here is that you've used 'minutes' in place of it's upper limit (what actually is the session's role), in this code for example
function increase() {
minutes++;
for (var i = 0; i < x.length; i++) {
x[i].innerHTML = minutes;
}
}
see, you are updating html by the value of 'minutes', and later you are reading that value into 'session' by that evil code:
session = x[1].innerHTML - 1;
So, why? Why you need to update the value of 'your' variable from html? You should only update the html according to the value of session var and not vice versa. Let's go on and make life simpler...
Let's keep the temporary value of minutes in 'minutes', let's also keep the upper limit in a variable session and, please, let's rename it to maxMinutes. Let's update the 'maxMinutes' only when user clicks '+' or '-' (NEVER from html).
<!DOCTYPE html>
<html>
<head>
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
</head>
<body>
<script>
var x = document.getElementsByClassName('session');
var maxMinutes = 0;
var minutes = maxMinutes;
var seconds = 0;
var timer;
var on = false;
function increase() {
if(on) {
return;
}
maxMinutes++;
minutes = maxMinutes;
x[0].innerHTML = maxMinutes;
x[1].innerHTML = minutes;
}
function decrease() {
if(on) {
return;
}
if(maxMinutes == 0)
{
return;
}
maxMinutes--;
minutes=maxMinutes;
x[0].innerHTML = maxMinutes;
x[1].innerHTML = minutes;
}
function periodicRun() {
if (seconds > 0) {
seconds--;
}
else if(seconds == 0)
{
if(minutes > 0) {
minutes--;
seconds = 60;
}
else
{
stopTimer(timer);
return;
}
}
x[1].innerHTML = minutes + ':' + seconds;
}
function stoptimer() {
clearInterval(timer);
}
function general() {
if (on) {
on = false;
stoptimer();
} else {
on = true;
timer = setInterval(periodicRun, 500);
}
}
$(document).ready(function(){
increase();
});
</script>
<div class='session'></div>
<div id='increase' onclick='decrease()'>-</div>
<div id='increase' onclick='increase()'>+</div>
<div class='session' onclick='general()'></div>
</body>
</html>
Note, that the only place where the maxLimits get's assigned a value is in increase() and decrease(). The 'minutes' and html are in their turn being updated according to maxMinutes.
Good Luck!

Break while loop with timer?

I was wondering is it possible to break a while loop with a timer?
looked on the internet but could not find a solution for it.
while (true) {
alert('hi');
} if (timer < 0) {
timer?
document.write('Time is up!');
break;
}
Thank you.
You should use setTimeout for this.
var timer = 3;
setTimeout(excuteMethod, 1000);
function excuteMethod() {
alert(timer + ' call');
timer--;
if (timer >= 0) setTimeout(excuteMethod, 1000);
}
Demo : http://jsfiddle.net/kishoresahas/9s9z7adt/
I'm not sure if this is the correct approach, but it works,
(function() {
var delay = 30;
var date = new Date();
var timer = date.setTime(date.getTime() + delay);
var count = 0;
function validate() {
var now = new Date();
if (+now > timer)
return false;
else
return true;
}
while (true) {
count++;
console.log(count);
if (!validate()) {
console.log("Time expired");
break;
}
// Fail safe.
if (count > 50000) {
console.log("Count breached")
break;
}
}
})()
You can change control value in timer function and break the loop.
var control = true;
while(control)
{
...
}
setTimeout(function(){
control = false;
}, delay); //delay is miliseconds
Or based on counter
var control = true,
counter = 10;
while(control){
...
}
// you can handle as count down
// count down counter every 1000 miliseconds
// after 10(counter start value) seconds
// change control value to false to break while loop
// and clear interval
var counterInterval = setInterval(function(){
counter--;
if(counter == 0)
{
control = false;
clearInterval(counterInterval);
}
},1000);

Stopping timer not working:

I have THIS timer in my project.
When it runs out, it shows a Time Up screen, which works fine.
But when the player is Game Over, i show the Game Over screen, but the timer keeps running and when it hits 00:00 then it switches to the Time Up screen.
How can i make this timer stop counting down and set to 00:00 again?
I tried adding a function like this:
CountDownTimer.prototype.stop = function() {
diff = 0;
this.running = false;
};
I also tried to change the innerHTML but its obvious that its just changing the numbers without stopping the timer and after a second it will show the count down again... I don't know what to call.
//Crazy Timer function start
function CountDownTimer(duration, granularity) {
this.duration = duration;
this.granularity = granularity || 1000;
this.tickFtns = [];
this.running = false;
}
CountDownTimer.prototype.start = function() {
if (this.running) {
return;
}
this.running = true;
var start = Date.now(),
that = this,
diff, obj;
(function timer() {
diff = that.duration - (((Date.now() - start) / 1000) | 0);
if (diff > 0) {
setTimeout(timer, that.granularity);
} else {
diff = 0;
that.running = false;
}
obj = CountDownTimer.parse(diff);
that.tickFtns.forEach(function(ftn) {
ftn.call(this, obj.minutes, obj.seconds);
}, that);
}());
};
CountDownTimer.prototype.onTick = function(ftn) {
if (typeof ftn === 'function') {
this.tickFtns.push(ftn);
}
return this;
};
CountDownTimer.prototype.expired = function() {
return !this.running;
};
CountDownTimer.parse = function(seconds) {
return {
'minutes': (seconds / 60) | 0,
'seconds': (seconds % 60) | 0
};
};
window.onload = function () {
var display = document.querySelector('#countDown'),
timer = new CountDownTimer(timerValue),
timeObj = CountDownTimer.parse(timerValue);
format(timeObj.minutes, timeObj.seconds);
timer.onTick(format).onTick(checkTime);
document.querySelector('#startBtn').addEventListener('click', function () {
timer.start();
});
function format(minutes, seconds) {
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
display.textContent = minutes + ':' + seconds;
}
function checkTime(){
if(this.expired()) {
timeUp();
document.querySelector('#startBtn').addEventListener('click', function () {
timer.start();
});
}
}
};
Instead of recursively calling setTimeout, try setInterval instead. You could then store a reference to the timer:
this.timer = setInterval(functionToRunAtInterval, this.granularity);
and kill it when the game finishes::
clearInterval(this.timer)
(see MDN's docs for more info on setInterval)
It's been a while and I'm not sure if you've figured it out but check out the fiddle below:
https://jsfiddle.net/f8rh3u85/1/
// until running is set to false the timer will keep running
if (that.running) {
if (diff > 0) {
setTimeout(timer, that.granularity);
} else {
diff = 0;
that.running = false;
}
obj = CountDownTimer.parse(diff);
that.tickFtns.forEach(function(ftn) {
ftn.call(this, obj.minutes, obj.seconds);
}, that);
}
I've added a button that causes running to be set to false which stops the timer.
Button:
<button id="stop">Game Over</button>
Code:
$( "#stop" ).click(function() {
timer.running = false;
});
So that should hopefully get you to where you need to be.
Similar to Tom Jenkins' answer, you need to cancel the next tick by avoiding the diff > 0 branch of your if statement. You could keep your code as it stands and use your suggested stop method, however, you'd need to change your logic around handling ticks to check that both running === true and a new param gameOver === false.
Ultimately, I think your problem is that no matter whether the timer is running or not, you'll always execute this code on a tick:
obj = CountDownTimer.parse(diff);
that.tickFtns.forEach(function(ftn) {
ftn.call(this, obj.minutes, obj.seconds);
}, that);
If you have a game over state, you probably don't want to call the provided callbacks, so add some conditional check in there.

Categories

Resources