Timer in JavaScript
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
1
down vote
favorite
This is my first JavaScript application (it's inspired by this timer). How could I improve it?
I had to set the setInterval()
to 980ms instead of 1000ms because it was slower than a real clock for some reason.
The full app
Main JS:
var btn1 = document.getElementById('start');
var btn2 = document.getElementById('pause');
var btn3 = document.getElementById('restart')
var h1 = document.getElementById('h1');
var h1Clr = h1.style.color;
var h1Nmb = 0;
var h2 = document.getElementById('h2');
var h2Clr = h1.style.color;
var h2Nmb = 0;
var m1 = document.getElementById('m1');
var m1Clr = h1.style.color;
var m1Nmb = 0;
var m2 = document.getElementById('m2');
var m2Clr = h1.style.color;
var m2Nmb = 0;
var s1 = document.getElementById('s1');
var s1Clr = h1.style.color;
var s1Nmb = 0;
var s2 = document.getElementById('s2');
var s2Clr = h1.style.color;
var s2Nmb = 0;
window.addEventListener("keydown", function (e) keycode == 0))
s1.textContent = String.fromCharCode(e.keyCode);
s1.style.color = 'rgb(192, 33, 33)';
s1Clr = 'rgb(192, 33, 33)';
window.s1Nmb = Number(s1.textContent);
else if (m2Clr == 'white' && (isNmbr );
var myVar;
btn1.onclick = function ()
window.myVar = setInterval(myTimer, 980);
btn2.onclick = function ()
clearInterval(window.myVar);
btn3.onclick = function ()
window.location.reload();
function myTimer()
//s2
if (s2Nmb != 0)
if (s2Nmb == 0)
s2Nmb = 10;
;
s2Nmb--;
s2.textContent = s2Nmb;
else
s2Nmb = 9;
s2.textContent = s2Nmb;
if (s2Nmb == 0)
s2Nmb = 10;
;
//s1
if (s2Nmb == 9)
if (s1Nmb == 0)
s1Nmb = 6;
;
s1Nmb--;
s1.textContent = s1Nmb;
//section Minutes
//m2
if (s1Nmb == 5 && s2Nmb == 9)
if (m2Nmb == 0)
m2Nmb = 10;
;
m2Nmb--;
m2.textContent = m2Nmb;
//m1
if (m2Nmb == 9 && s1Nmb == 5 && s2Nmb == 9)
if (m1Nmb == 0)
m1Nmb = 6;
;
m1Nmb--;
m1.textContent = m1Nmb;
//h2
if (m1Nmb == 5 && m2Nmb == 9 && s1Nmb == 5 && s2Nmb == 9)
if (h2Nmb == 0)
h2Nmb = 10;
;
h2Nmb--;
h2.textContent = h2Nmb;
//h1
if (h2Nmb == 9 && m1Nmb == 5 && m2Nmb == 9 && s1Nmb == 5 && s2Nmb == 9)
if (h1Nmb == 0)
h1Nmb = 6;
;
h1Nmb--;
h1.textContent = h1Nmb;
//alert
if (h1Nmb == 0 && h2Nmb == 0 && m1Nmb == 0 && m2Nmb == 0 && s1Nmb == 0 && s2Nmb == 0)
clearInterval(window.myVar);
var snd = new Audio("alarm_beep.wav");
snd.volume = 0.3;
snd.play();
;
javascript timer
add a comment |Â
up vote
1
down vote
favorite
This is my first JavaScript application (it's inspired by this timer). How could I improve it?
I had to set the setInterval()
to 980ms instead of 1000ms because it was slower than a real clock for some reason.
The full app
Main JS:
var btn1 = document.getElementById('start');
var btn2 = document.getElementById('pause');
var btn3 = document.getElementById('restart')
var h1 = document.getElementById('h1');
var h1Clr = h1.style.color;
var h1Nmb = 0;
var h2 = document.getElementById('h2');
var h2Clr = h1.style.color;
var h2Nmb = 0;
var m1 = document.getElementById('m1');
var m1Clr = h1.style.color;
var m1Nmb = 0;
var m2 = document.getElementById('m2');
var m2Clr = h1.style.color;
var m2Nmb = 0;
var s1 = document.getElementById('s1');
var s1Clr = h1.style.color;
var s1Nmb = 0;
var s2 = document.getElementById('s2');
var s2Clr = h1.style.color;
var s2Nmb = 0;
window.addEventListener("keydown", function (e) keycode == 0))
s1.textContent = String.fromCharCode(e.keyCode);
s1.style.color = 'rgb(192, 33, 33)';
s1Clr = 'rgb(192, 33, 33)';
window.s1Nmb = Number(s1.textContent);
else if (m2Clr == 'white' && (isNmbr );
var myVar;
btn1.onclick = function ()
window.myVar = setInterval(myTimer, 980);
btn2.onclick = function ()
clearInterval(window.myVar);
btn3.onclick = function ()
window.location.reload();
function myTimer()
//s2
if (s2Nmb != 0)
if (s2Nmb == 0)
s2Nmb = 10;
;
s2Nmb--;
s2.textContent = s2Nmb;
else
s2Nmb = 9;
s2.textContent = s2Nmb;
if (s2Nmb == 0)
s2Nmb = 10;
;
//s1
if (s2Nmb == 9)
if (s1Nmb == 0)
s1Nmb = 6;
;
s1Nmb--;
s1.textContent = s1Nmb;
//section Minutes
//m2
if (s1Nmb == 5 && s2Nmb == 9)
if (m2Nmb == 0)
m2Nmb = 10;
;
m2Nmb--;
m2.textContent = m2Nmb;
//m1
if (m2Nmb == 9 && s1Nmb == 5 && s2Nmb == 9)
if (m1Nmb == 0)
m1Nmb = 6;
;
m1Nmb--;
m1.textContent = m1Nmb;
//h2
if (m1Nmb == 5 && m2Nmb == 9 && s1Nmb == 5 && s2Nmb == 9)
if (h2Nmb == 0)
h2Nmb = 10;
;
h2Nmb--;
h2.textContent = h2Nmb;
//h1
if (h2Nmb == 9 && m1Nmb == 5 && m2Nmb == 9 && s1Nmb == 5 && s2Nmb == 9)
if (h1Nmb == 0)
h1Nmb = 6;
;
h1Nmb--;
h1.textContent = h1Nmb;
//alert
if (h1Nmb == 0 && h2Nmb == 0 && m1Nmb == 0 && m2Nmb == 0 && s1Nmb == 0 && s2Nmb == 0)
clearInterval(window.myVar);
var snd = new Audio("alarm_beep.wav");
snd.volume = 0.3;
snd.play();
;
javascript timer
add a comment |Â
up vote
1
down vote
favorite
up vote
1
down vote
favorite
This is my first JavaScript application (it's inspired by this timer). How could I improve it?
I had to set the setInterval()
to 980ms instead of 1000ms because it was slower than a real clock for some reason.
The full app
Main JS:
var btn1 = document.getElementById('start');
var btn2 = document.getElementById('pause');
var btn3 = document.getElementById('restart')
var h1 = document.getElementById('h1');
var h1Clr = h1.style.color;
var h1Nmb = 0;
var h2 = document.getElementById('h2');
var h2Clr = h1.style.color;
var h2Nmb = 0;
var m1 = document.getElementById('m1');
var m1Clr = h1.style.color;
var m1Nmb = 0;
var m2 = document.getElementById('m2');
var m2Clr = h1.style.color;
var m2Nmb = 0;
var s1 = document.getElementById('s1');
var s1Clr = h1.style.color;
var s1Nmb = 0;
var s2 = document.getElementById('s2');
var s2Clr = h1.style.color;
var s2Nmb = 0;
window.addEventListener("keydown", function (e) keycode == 0))
s1.textContent = String.fromCharCode(e.keyCode);
s1.style.color = 'rgb(192, 33, 33)';
s1Clr = 'rgb(192, 33, 33)';
window.s1Nmb = Number(s1.textContent);
else if (m2Clr == 'white' && (isNmbr );
var myVar;
btn1.onclick = function ()
window.myVar = setInterval(myTimer, 980);
btn2.onclick = function ()
clearInterval(window.myVar);
btn3.onclick = function ()
window.location.reload();
function myTimer()
//s2
if (s2Nmb != 0)
if (s2Nmb == 0)
s2Nmb = 10;
;
s2Nmb--;
s2.textContent = s2Nmb;
else
s2Nmb = 9;
s2.textContent = s2Nmb;
if (s2Nmb == 0)
s2Nmb = 10;
;
//s1
if (s2Nmb == 9)
if (s1Nmb == 0)
s1Nmb = 6;
;
s1Nmb--;
s1.textContent = s1Nmb;
//section Minutes
//m2
if (s1Nmb == 5 && s2Nmb == 9)
if (m2Nmb == 0)
m2Nmb = 10;
;
m2Nmb--;
m2.textContent = m2Nmb;
//m1
if (m2Nmb == 9 && s1Nmb == 5 && s2Nmb == 9)
if (m1Nmb == 0)
m1Nmb = 6;
;
m1Nmb--;
m1.textContent = m1Nmb;
//h2
if (m1Nmb == 5 && m2Nmb == 9 && s1Nmb == 5 && s2Nmb == 9)
if (h2Nmb == 0)
h2Nmb = 10;
;
h2Nmb--;
h2.textContent = h2Nmb;
//h1
if (h2Nmb == 9 && m1Nmb == 5 && m2Nmb == 9 && s1Nmb == 5 && s2Nmb == 9)
if (h1Nmb == 0)
h1Nmb = 6;
;
h1Nmb--;
h1.textContent = h1Nmb;
//alert
if (h1Nmb == 0 && h2Nmb == 0 && m1Nmb == 0 && m2Nmb == 0 && s1Nmb == 0 && s2Nmb == 0)
clearInterval(window.myVar);
var snd = new Audio("alarm_beep.wav");
snd.volume = 0.3;
snd.play();
;
javascript timer
This is my first JavaScript application (it's inspired by this timer). How could I improve it?
I had to set the setInterval()
to 980ms instead of 1000ms because it was slower than a real clock for some reason.
The full app
Main JS:
var btn1 = document.getElementById('start');
var btn2 = document.getElementById('pause');
var btn3 = document.getElementById('restart')
var h1 = document.getElementById('h1');
var h1Clr = h1.style.color;
var h1Nmb = 0;
var h2 = document.getElementById('h2');
var h2Clr = h1.style.color;
var h2Nmb = 0;
var m1 = document.getElementById('m1');
var m1Clr = h1.style.color;
var m1Nmb = 0;
var m2 = document.getElementById('m2');
var m2Clr = h1.style.color;
var m2Nmb = 0;
var s1 = document.getElementById('s1');
var s1Clr = h1.style.color;
var s1Nmb = 0;
var s2 = document.getElementById('s2');
var s2Clr = h1.style.color;
var s2Nmb = 0;
window.addEventListener("keydown", function (e) keycode == 0))
s1.textContent = String.fromCharCode(e.keyCode);
s1.style.color = 'rgb(192, 33, 33)';
s1Clr = 'rgb(192, 33, 33)';
window.s1Nmb = Number(s1.textContent);
else if (m2Clr == 'white' && (isNmbr );
var myVar;
btn1.onclick = function ()
window.myVar = setInterval(myTimer, 980);
btn2.onclick = function ()
clearInterval(window.myVar);
btn3.onclick = function ()
window.location.reload();
function myTimer()
//s2
if (s2Nmb != 0)
if (s2Nmb == 0)
s2Nmb = 10;
;
s2Nmb--;
s2.textContent = s2Nmb;
else
s2Nmb = 9;
s2.textContent = s2Nmb;
if (s2Nmb == 0)
s2Nmb = 10;
;
//s1
if (s2Nmb == 9)
if (s1Nmb == 0)
s1Nmb = 6;
;
s1Nmb--;
s1.textContent = s1Nmb;
//section Minutes
//m2
if (s1Nmb == 5 && s2Nmb == 9)
if (m2Nmb == 0)
m2Nmb = 10;
;
m2Nmb--;
m2.textContent = m2Nmb;
//m1
if (m2Nmb == 9 && s1Nmb == 5 && s2Nmb == 9)
if (m1Nmb == 0)
m1Nmb = 6;
;
m1Nmb--;
m1.textContent = m1Nmb;
//h2
if (m1Nmb == 5 && m2Nmb == 9 && s1Nmb == 5 && s2Nmb == 9)
if (h2Nmb == 0)
h2Nmb = 10;
;
h2Nmb--;
h2.textContent = h2Nmb;
//h1
if (h2Nmb == 9 && m1Nmb == 5 && m2Nmb == 9 && s1Nmb == 5 && s2Nmb == 9)
if (h1Nmb == 0)
h1Nmb = 6;
;
h1Nmb--;
h1.textContent = h1Nmb;
//alert
if (h1Nmb == 0 && h2Nmb == 0 && m1Nmb == 0 && m2Nmb == 0 && s1Nmb == 0 && s2Nmb == 0)
clearInterval(window.myVar);
var snd = new Audio("alarm_beep.wav");
snd.volume = 0.3;
snd.play();
;
javascript timer
edited Feb 9 at 13:19
Jamalâ¦
30.1k11114225
30.1k11114225
asked Feb 8 at 19:42
b4l4g3
82
82
add a comment |Â
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
2
down vote
accepted
Timer accuracy
JS timers only guarantee that at least delay
milliseconds elapsed before your function is called. It does not guarantee the exact time when. What happens under the hood when you call setInterval
or setTimeout
is that your callback gets queued for execution. JS only picks the callback up when it has cleared the execution stack. If something is holding up the JS engine, that callback maybe called later than expected.
Timer implementation
Implementation of timers also vary. If I remember correctly, the fastest interval is never less than 4ms for regular timers. So a 0ms delay is never really 0ms. Browser vendors may slow down or stop timers entirely timers when the window is not in focus or when it detects inactivity to save battery.
Guaranteeing timer accuracy
One trick to guarantee time accuracy is to never actually rely on the timer. Instead, you look at the real time. To do that, you create a timer that runs faster than a second. On each invocation, call Date.now()
and compare the timestamp.
// Implementing a timer using just the timer
var intervalInput = document.getElementById('interval')
var intervalSeconds = 0
setInterval(() =>
intervalInput.value = ++intervalSeconds
, 1000)
// Implementing a by computing seconds from real time since the start.
var realInput = document.getElementById('real')
var start = Date.now()
setInterval(() =>
// Compute how many seconds since we started
realInput.value = Math.floor((Date.now() - start) / 1000)
, 200)
<label>Interval seconds:</label> <input id="interval">
<label>Real seconds:</label> <input id="real">
Time representation
A naive implementation of a timer involves storing the the units (seconds, minutes, hours, etc) in separate variables, and write a cascading logic (i.e. from 59s to 0s, increments mins) to update the values. This is not really scalable once you start adding more units. It's also hard to debug since you need to step through that logic.
A better way to do this is to store time in a single value, like milliseconds, and compute the units off of it. The units are just functions of the millisecond value. A countdown timer is simply a future timestamp as start value minus the current timestamp.
const hoursInput = document.getElementById('hours')
const minutesInput = document.getElementById('minutes')
const secondsInput = document.getElementById('seconds')
const startButton = document.getElementById('start')
const pauseButton = document.getElementById('pause')
const resetButton = document.getElementById('reset')
const pad = v => `00$v`.slice(-2)
// Functions that compute units from a millisecond value
const hours = m => Math.floor((m / 3600000) % 24)
const minutes = m => Math.floor((m / 60000) % 60)
const seconds = m => Math.floor((m / 1000) % 60)
// Render function
const render = v =>
hoursInput.value = pad(hours(v))
minutesInput.value = pad(minutes(v))
secondsInput.value = pad(seconds(v))
// This is the value you ask from the user
const duration = 3610000
let remaining = duration
let timer = null
let end = 0
startButton.addEventListener('click', e =>
if (timer) return
// Get end timestamp based on current time + remaining time
end = Date.now() + remaining
// Render the remaining time every 16ms (approx 60fps)
timer = setInterval(() =>
render(end - Date.now())
, 16)
)
pauseButton.addEventListener('click', e =>
// Do nothing if timer not running
if(!timer) return
// Otherwise, clear timer
clearInterval(timer)
timer = null
// Note the remaining time
remaining = end - Date.now()
render(remaining)
)
resetButton.addEventListener('click', e =>
// Do nothing if timer not running
if(!timer) return
// Otherwise, clear timer
clearInterval(timer)
timer = null
// Reset remaining to original duration
remaining = duration
render(remaining)
)
render(remaining)
<input id="hours">h <input id="minutes">m <input id="seconds">s
<button type="button" id="start">Start</button>
<button type="button" id="pause">Pause</button>
<button type="button" id="reset">Reset</button>
Lastly, one minor flaw in your countdown timer is that your start button doesn't check if the timer is already running. This causes it to spawn another JS timer, doubling the decrement.
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
accepted
Timer accuracy
JS timers only guarantee that at least delay
milliseconds elapsed before your function is called. It does not guarantee the exact time when. What happens under the hood when you call setInterval
or setTimeout
is that your callback gets queued for execution. JS only picks the callback up when it has cleared the execution stack. If something is holding up the JS engine, that callback maybe called later than expected.
Timer implementation
Implementation of timers also vary. If I remember correctly, the fastest interval is never less than 4ms for regular timers. So a 0ms delay is never really 0ms. Browser vendors may slow down or stop timers entirely timers when the window is not in focus or when it detects inactivity to save battery.
Guaranteeing timer accuracy
One trick to guarantee time accuracy is to never actually rely on the timer. Instead, you look at the real time. To do that, you create a timer that runs faster than a second. On each invocation, call Date.now()
and compare the timestamp.
// Implementing a timer using just the timer
var intervalInput = document.getElementById('interval')
var intervalSeconds = 0
setInterval(() =>
intervalInput.value = ++intervalSeconds
, 1000)
// Implementing a by computing seconds from real time since the start.
var realInput = document.getElementById('real')
var start = Date.now()
setInterval(() =>
// Compute how many seconds since we started
realInput.value = Math.floor((Date.now() - start) / 1000)
, 200)
<label>Interval seconds:</label> <input id="interval">
<label>Real seconds:</label> <input id="real">
Time representation
A naive implementation of a timer involves storing the the units (seconds, minutes, hours, etc) in separate variables, and write a cascading logic (i.e. from 59s to 0s, increments mins) to update the values. This is not really scalable once you start adding more units. It's also hard to debug since you need to step through that logic.
A better way to do this is to store time in a single value, like milliseconds, and compute the units off of it. The units are just functions of the millisecond value. A countdown timer is simply a future timestamp as start value minus the current timestamp.
const hoursInput = document.getElementById('hours')
const minutesInput = document.getElementById('minutes')
const secondsInput = document.getElementById('seconds')
const startButton = document.getElementById('start')
const pauseButton = document.getElementById('pause')
const resetButton = document.getElementById('reset')
const pad = v => `00$v`.slice(-2)
// Functions that compute units from a millisecond value
const hours = m => Math.floor((m / 3600000) % 24)
const minutes = m => Math.floor((m / 60000) % 60)
const seconds = m => Math.floor((m / 1000) % 60)
// Render function
const render = v =>
hoursInput.value = pad(hours(v))
minutesInput.value = pad(minutes(v))
secondsInput.value = pad(seconds(v))
// This is the value you ask from the user
const duration = 3610000
let remaining = duration
let timer = null
let end = 0
startButton.addEventListener('click', e =>
if (timer) return
// Get end timestamp based on current time + remaining time
end = Date.now() + remaining
// Render the remaining time every 16ms (approx 60fps)
timer = setInterval(() =>
render(end - Date.now())
, 16)
)
pauseButton.addEventListener('click', e =>
// Do nothing if timer not running
if(!timer) return
// Otherwise, clear timer
clearInterval(timer)
timer = null
// Note the remaining time
remaining = end - Date.now()
render(remaining)
)
resetButton.addEventListener('click', e =>
// Do nothing if timer not running
if(!timer) return
// Otherwise, clear timer
clearInterval(timer)
timer = null
// Reset remaining to original duration
remaining = duration
render(remaining)
)
render(remaining)
<input id="hours">h <input id="minutes">m <input id="seconds">s
<button type="button" id="start">Start</button>
<button type="button" id="pause">Pause</button>
<button type="button" id="reset">Reset</button>
Lastly, one minor flaw in your countdown timer is that your start button doesn't check if the timer is already running. This causes it to spawn another JS timer, doubling the decrement.
add a comment |Â
up vote
2
down vote
accepted
Timer accuracy
JS timers only guarantee that at least delay
milliseconds elapsed before your function is called. It does not guarantee the exact time when. What happens under the hood when you call setInterval
or setTimeout
is that your callback gets queued for execution. JS only picks the callback up when it has cleared the execution stack. If something is holding up the JS engine, that callback maybe called later than expected.
Timer implementation
Implementation of timers also vary. If I remember correctly, the fastest interval is never less than 4ms for regular timers. So a 0ms delay is never really 0ms. Browser vendors may slow down or stop timers entirely timers when the window is not in focus or when it detects inactivity to save battery.
Guaranteeing timer accuracy
One trick to guarantee time accuracy is to never actually rely on the timer. Instead, you look at the real time. To do that, you create a timer that runs faster than a second. On each invocation, call Date.now()
and compare the timestamp.
// Implementing a timer using just the timer
var intervalInput = document.getElementById('interval')
var intervalSeconds = 0
setInterval(() =>
intervalInput.value = ++intervalSeconds
, 1000)
// Implementing a by computing seconds from real time since the start.
var realInput = document.getElementById('real')
var start = Date.now()
setInterval(() =>
// Compute how many seconds since we started
realInput.value = Math.floor((Date.now() - start) / 1000)
, 200)
<label>Interval seconds:</label> <input id="interval">
<label>Real seconds:</label> <input id="real">
Time representation
A naive implementation of a timer involves storing the the units (seconds, minutes, hours, etc) in separate variables, and write a cascading logic (i.e. from 59s to 0s, increments mins) to update the values. This is not really scalable once you start adding more units. It's also hard to debug since you need to step through that logic.
A better way to do this is to store time in a single value, like milliseconds, and compute the units off of it. The units are just functions of the millisecond value. A countdown timer is simply a future timestamp as start value minus the current timestamp.
const hoursInput = document.getElementById('hours')
const minutesInput = document.getElementById('minutes')
const secondsInput = document.getElementById('seconds')
const startButton = document.getElementById('start')
const pauseButton = document.getElementById('pause')
const resetButton = document.getElementById('reset')
const pad = v => `00$v`.slice(-2)
// Functions that compute units from a millisecond value
const hours = m => Math.floor((m / 3600000) % 24)
const minutes = m => Math.floor((m / 60000) % 60)
const seconds = m => Math.floor((m / 1000) % 60)
// Render function
const render = v =>
hoursInput.value = pad(hours(v))
minutesInput.value = pad(minutes(v))
secondsInput.value = pad(seconds(v))
// This is the value you ask from the user
const duration = 3610000
let remaining = duration
let timer = null
let end = 0
startButton.addEventListener('click', e =>
if (timer) return
// Get end timestamp based on current time + remaining time
end = Date.now() + remaining
// Render the remaining time every 16ms (approx 60fps)
timer = setInterval(() =>
render(end - Date.now())
, 16)
)
pauseButton.addEventListener('click', e =>
// Do nothing if timer not running
if(!timer) return
// Otherwise, clear timer
clearInterval(timer)
timer = null
// Note the remaining time
remaining = end - Date.now()
render(remaining)
)
resetButton.addEventListener('click', e =>
// Do nothing if timer not running
if(!timer) return
// Otherwise, clear timer
clearInterval(timer)
timer = null
// Reset remaining to original duration
remaining = duration
render(remaining)
)
render(remaining)
<input id="hours">h <input id="minutes">m <input id="seconds">s
<button type="button" id="start">Start</button>
<button type="button" id="pause">Pause</button>
<button type="button" id="reset">Reset</button>
Lastly, one minor flaw in your countdown timer is that your start button doesn't check if the timer is already running. This causes it to spawn another JS timer, doubling the decrement.
add a comment |Â
up vote
2
down vote
accepted
up vote
2
down vote
accepted
Timer accuracy
JS timers only guarantee that at least delay
milliseconds elapsed before your function is called. It does not guarantee the exact time when. What happens under the hood when you call setInterval
or setTimeout
is that your callback gets queued for execution. JS only picks the callback up when it has cleared the execution stack. If something is holding up the JS engine, that callback maybe called later than expected.
Timer implementation
Implementation of timers also vary. If I remember correctly, the fastest interval is never less than 4ms for regular timers. So a 0ms delay is never really 0ms. Browser vendors may slow down or stop timers entirely timers when the window is not in focus or when it detects inactivity to save battery.
Guaranteeing timer accuracy
One trick to guarantee time accuracy is to never actually rely on the timer. Instead, you look at the real time. To do that, you create a timer that runs faster than a second. On each invocation, call Date.now()
and compare the timestamp.
// Implementing a timer using just the timer
var intervalInput = document.getElementById('interval')
var intervalSeconds = 0
setInterval(() =>
intervalInput.value = ++intervalSeconds
, 1000)
// Implementing a by computing seconds from real time since the start.
var realInput = document.getElementById('real')
var start = Date.now()
setInterval(() =>
// Compute how many seconds since we started
realInput.value = Math.floor((Date.now() - start) / 1000)
, 200)
<label>Interval seconds:</label> <input id="interval">
<label>Real seconds:</label> <input id="real">
Time representation
A naive implementation of a timer involves storing the the units (seconds, minutes, hours, etc) in separate variables, and write a cascading logic (i.e. from 59s to 0s, increments mins) to update the values. This is not really scalable once you start adding more units. It's also hard to debug since you need to step through that logic.
A better way to do this is to store time in a single value, like milliseconds, and compute the units off of it. The units are just functions of the millisecond value. A countdown timer is simply a future timestamp as start value minus the current timestamp.
const hoursInput = document.getElementById('hours')
const minutesInput = document.getElementById('minutes')
const secondsInput = document.getElementById('seconds')
const startButton = document.getElementById('start')
const pauseButton = document.getElementById('pause')
const resetButton = document.getElementById('reset')
const pad = v => `00$v`.slice(-2)
// Functions that compute units from a millisecond value
const hours = m => Math.floor((m / 3600000) % 24)
const minutes = m => Math.floor((m / 60000) % 60)
const seconds = m => Math.floor((m / 1000) % 60)
// Render function
const render = v =>
hoursInput.value = pad(hours(v))
minutesInput.value = pad(minutes(v))
secondsInput.value = pad(seconds(v))
// This is the value you ask from the user
const duration = 3610000
let remaining = duration
let timer = null
let end = 0
startButton.addEventListener('click', e =>
if (timer) return
// Get end timestamp based on current time + remaining time
end = Date.now() + remaining
// Render the remaining time every 16ms (approx 60fps)
timer = setInterval(() =>
render(end - Date.now())
, 16)
)
pauseButton.addEventListener('click', e =>
// Do nothing if timer not running
if(!timer) return
// Otherwise, clear timer
clearInterval(timer)
timer = null
// Note the remaining time
remaining = end - Date.now()
render(remaining)
)
resetButton.addEventListener('click', e =>
// Do nothing if timer not running
if(!timer) return
// Otherwise, clear timer
clearInterval(timer)
timer = null
// Reset remaining to original duration
remaining = duration
render(remaining)
)
render(remaining)
<input id="hours">h <input id="minutes">m <input id="seconds">s
<button type="button" id="start">Start</button>
<button type="button" id="pause">Pause</button>
<button type="button" id="reset">Reset</button>
Lastly, one minor flaw in your countdown timer is that your start button doesn't check if the timer is already running. This causes it to spawn another JS timer, doubling the decrement.
Timer accuracy
JS timers only guarantee that at least delay
milliseconds elapsed before your function is called. It does not guarantee the exact time when. What happens under the hood when you call setInterval
or setTimeout
is that your callback gets queued for execution. JS only picks the callback up when it has cleared the execution stack. If something is holding up the JS engine, that callback maybe called later than expected.
Timer implementation
Implementation of timers also vary. If I remember correctly, the fastest interval is never less than 4ms for regular timers. So a 0ms delay is never really 0ms. Browser vendors may slow down or stop timers entirely timers when the window is not in focus or when it detects inactivity to save battery.
Guaranteeing timer accuracy
One trick to guarantee time accuracy is to never actually rely on the timer. Instead, you look at the real time. To do that, you create a timer that runs faster than a second. On each invocation, call Date.now()
and compare the timestamp.
// Implementing a timer using just the timer
var intervalInput = document.getElementById('interval')
var intervalSeconds = 0
setInterval(() =>
intervalInput.value = ++intervalSeconds
, 1000)
// Implementing a by computing seconds from real time since the start.
var realInput = document.getElementById('real')
var start = Date.now()
setInterval(() =>
// Compute how many seconds since we started
realInput.value = Math.floor((Date.now() - start) / 1000)
, 200)
<label>Interval seconds:</label> <input id="interval">
<label>Real seconds:</label> <input id="real">
Time representation
A naive implementation of a timer involves storing the the units (seconds, minutes, hours, etc) in separate variables, and write a cascading logic (i.e. from 59s to 0s, increments mins) to update the values. This is not really scalable once you start adding more units. It's also hard to debug since you need to step through that logic.
A better way to do this is to store time in a single value, like milliseconds, and compute the units off of it. The units are just functions of the millisecond value. A countdown timer is simply a future timestamp as start value minus the current timestamp.
const hoursInput = document.getElementById('hours')
const minutesInput = document.getElementById('minutes')
const secondsInput = document.getElementById('seconds')
const startButton = document.getElementById('start')
const pauseButton = document.getElementById('pause')
const resetButton = document.getElementById('reset')
const pad = v => `00$v`.slice(-2)
// Functions that compute units from a millisecond value
const hours = m => Math.floor((m / 3600000) % 24)
const minutes = m => Math.floor((m / 60000) % 60)
const seconds = m => Math.floor((m / 1000) % 60)
// Render function
const render = v =>
hoursInput.value = pad(hours(v))
minutesInput.value = pad(minutes(v))
secondsInput.value = pad(seconds(v))
// This is the value you ask from the user
const duration = 3610000
let remaining = duration
let timer = null
let end = 0
startButton.addEventListener('click', e =>
if (timer) return
// Get end timestamp based on current time + remaining time
end = Date.now() + remaining
// Render the remaining time every 16ms (approx 60fps)
timer = setInterval(() =>
render(end - Date.now())
, 16)
)
pauseButton.addEventListener('click', e =>
// Do nothing if timer not running
if(!timer) return
// Otherwise, clear timer
clearInterval(timer)
timer = null
// Note the remaining time
remaining = end - Date.now()
render(remaining)
)
resetButton.addEventListener('click', e =>
// Do nothing if timer not running
if(!timer) return
// Otherwise, clear timer
clearInterval(timer)
timer = null
// Reset remaining to original duration
remaining = duration
render(remaining)
)
render(remaining)
<input id="hours">h <input id="minutes">m <input id="seconds">s
<button type="button" id="start">Start</button>
<button type="button" id="pause">Pause</button>
<button type="button" id="reset">Reset</button>
Lastly, one minor flaw in your countdown timer is that your start button doesn't check if the timer is already running. This causes it to spawn another JS timer, doubling the decrement.
// Implementing a timer using just the timer
var intervalInput = document.getElementById('interval')
var intervalSeconds = 0
setInterval(() =>
intervalInput.value = ++intervalSeconds
, 1000)
// Implementing a by computing seconds from real time since the start.
var realInput = document.getElementById('real')
var start = Date.now()
setInterval(() =>
// Compute how many seconds since we started
realInput.value = Math.floor((Date.now() - start) / 1000)
, 200)
<label>Interval seconds:</label> <input id="interval">
<label>Real seconds:</label> <input id="real">
// Implementing a timer using just the timer
var intervalInput = document.getElementById('interval')
var intervalSeconds = 0
setInterval(() =>
intervalInput.value = ++intervalSeconds
, 1000)
// Implementing a by computing seconds from real time since the start.
var realInput = document.getElementById('real')
var start = Date.now()
setInterval(() =>
// Compute how many seconds since we started
realInput.value = Math.floor((Date.now() - start) / 1000)
, 200)
<label>Interval seconds:</label> <input id="interval">
<label>Real seconds:</label> <input id="real">
const hoursInput = document.getElementById('hours')
const minutesInput = document.getElementById('minutes')
const secondsInput = document.getElementById('seconds')
const startButton = document.getElementById('start')
const pauseButton = document.getElementById('pause')
const resetButton = document.getElementById('reset')
const pad = v => `00$v`.slice(-2)
// Functions that compute units from a millisecond value
const hours = m => Math.floor((m / 3600000) % 24)
const minutes = m => Math.floor((m / 60000) % 60)
const seconds = m => Math.floor((m / 1000) % 60)
// Render function
const render = v =>
hoursInput.value = pad(hours(v))
minutesInput.value = pad(minutes(v))
secondsInput.value = pad(seconds(v))
// This is the value you ask from the user
const duration = 3610000
let remaining = duration
let timer = null
let end = 0
startButton.addEventListener('click', e =>
if (timer) return
// Get end timestamp based on current time + remaining time
end = Date.now() + remaining
// Render the remaining time every 16ms (approx 60fps)
timer = setInterval(() =>
render(end - Date.now())
, 16)
)
pauseButton.addEventListener('click', e =>
// Do nothing if timer not running
if(!timer) return
// Otherwise, clear timer
clearInterval(timer)
timer = null
// Note the remaining time
remaining = end - Date.now()
render(remaining)
)
resetButton.addEventListener('click', e =>
// Do nothing if timer not running
if(!timer) return
// Otherwise, clear timer
clearInterval(timer)
timer = null
// Reset remaining to original duration
remaining = duration
render(remaining)
)
render(remaining)
<input id="hours">h <input id="minutes">m <input id="seconds">s
<button type="button" id="start">Start</button>
<button type="button" id="pause">Pause</button>
<button type="button" id="reset">Reset</button>
const hoursInput = document.getElementById('hours')
const minutesInput = document.getElementById('minutes')
const secondsInput = document.getElementById('seconds')
const startButton = document.getElementById('start')
const pauseButton = document.getElementById('pause')
const resetButton = document.getElementById('reset')
const pad = v => `00$v`.slice(-2)
// Functions that compute units from a millisecond value
const hours = m => Math.floor((m / 3600000) % 24)
const minutes = m => Math.floor((m / 60000) % 60)
const seconds = m => Math.floor((m / 1000) % 60)
// Render function
const render = v =>
hoursInput.value = pad(hours(v))
minutesInput.value = pad(minutes(v))
secondsInput.value = pad(seconds(v))
// This is the value you ask from the user
const duration = 3610000
let remaining = duration
let timer = null
let end = 0
startButton.addEventListener('click', e =>
if (timer) return
// Get end timestamp based on current time + remaining time
end = Date.now() + remaining
// Render the remaining time every 16ms (approx 60fps)
timer = setInterval(() =>
render(end - Date.now())
, 16)
)
pauseButton.addEventListener('click', e =>
// Do nothing if timer not running
if(!timer) return
// Otherwise, clear timer
clearInterval(timer)
timer = null
// Note the remaining time
remaining = end - Date.now()
render(remaining)
)
resetButton.addEventListener('click', e =>
// Do nothing if timer not running
if(!timer) return
// Otherwise, clear timer
clearInterval(timer)
timer = null
// Reset remaining to original duration
remaining = duration
render(remaining)
)
render(remaining)
<input id="hours">h <input id="minutes">m <input id="seconds">s
<button type="button" id="start">Start</button>
<button type="button" id="pause">Pause</button>
<button type="button" id="reset">Reset</button>
edited Feb 9 at 3:44
answered Feb 9 at 3:28
Joseph
22.1k21833
22.1k21833
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f187117%2ftimer-in-javascript%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password