Search code examples
javascripthtmlsetintervalstopwatchmilliseconds

JavaScript stopwatch being slower than expected


In my quest to become a JavaScript developer and as an F1 fan, I had to make a stopwatch, to track reaction time. The problem I stumbled upon, and it seems to be the case in many tutorials I've seen, is that you can't really be millisecond exact.

What I mean is, when I try to set an Interval for every 10 milliseconds, it works perfectly, but if I try to set an Interval for 1 millisecond, 1 second on my page is like 5 seconds in real life.

let clock = document.querySelector('.timer')
let interval = null;
let milliseconds = 0;
let seconds =0;



window.addEventListener('click', () => {
        interval = setInterval(startClock,10)
})


document.addEventListener('keyup', event => {
    if (event.code === 'Space') {
        clearInterval(interval);
        setTimeout(()=>{
            alert(`${seconds}:${milliseconds}`);
        },50) 
    }
})

function startClock(){
    milliseconds += 10;
    if(milliseconds==1000){
        milliseconds = 0;
        seconds++;
    }

    let sec = seconds.toString();
    let ms = milliseconds.toString();
    if(seconds<10){
        sec = `0${seconds}`;
    }

    if(milliseconds<100){
        ms = `0${milliseconds}`;
    }

    clock.innerHTML =`${sec}:${ms}`;
}
p{
    color: rgb(25, 25, 25);
    font-size: 170px;
    height: 120px;
    font-weight: bold;
}
<p class="timer">00:000</p>


Solution

  • Since you can't guarantee how long the code inside the interval will take to run, it's better to use the Date api. Instead of relying on the interval, you could run a loop that constantly calculates the ms between now and the start.

    Here's an example of this concept:

    // assume this runs when the stopwatch starts
    let startTime = new Date().getTime()
    
    // this should stop when the user stops the timer, but I'm using a for loop
    // for the example
    for(let i = 0; i < 100; i++) {
      let now = new Date().getTime()
      console.log("ms elapsed: " + (now - startTime))
    }
    

    Now it doesn't matter how fast the code is; it should always be accurate.