Timer in JavaScript

The name of the pictureThe name of the pictureThe name of the pictureClash 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();


;






share|improve this question



























    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();


    ;






    share|improve this question























      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();


      ;






      share|improve this question













      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();


      ;








      share|improve this question












      share|improve this question




      share|improve this question








      edited Feb 9 at 13:19









      Jamal♦

      30.1k11114225




      30.1k11114225









      asked Feb 8 at 19:42









      b4l4g3

      82




      82




















          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.






          share|improve this answer























            Your Answer




            StackExchange.ifUsing("editor", function ()
            return StackExchange.using("mathjaxEditing", function ()
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            );
            );
            , "mathjax-editing");

            StackExchange.ifUsing("editor", function ()
            StackExchange.using("externalEditor", function ()
            StackExchange.using("snippets", function ()
            StackExchange.snippets.init();
            );
            );
            , "code-snippets");

            StackExchange.ready(function()
            var channelOptions =
            tags: "".split(" "),
            id: "196"
            ;
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function()
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled)
            StackExchange.using("snippets", function()
            createEditor();
            );

            else
            createEditor();

            );

            function createEditor()
            StackExchange.prepareEditor(
            heartbeatType: 'answer',
            convertImagesToLinks: false,
            noModals: false,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            );



            );








             

            draft saved


            draft discarded


















            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






























            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.






            share|improve this answer



























              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.






              share|improve this answer

























                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.






                share|improve this answer















                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>






                share|improve this answer















                share|improve this answer



                share|improve this answer








                edited Feb 9 at 3:44


























                answered Feb 9 at 3:28









                Joseph

                22.1k21833




                22.1k21833






















                     

                    draft saved


                    draft discarded


























                     


                    draft saved


                    draft discarded














                    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













































































                    Popular posts from this blog

                    Greedy Best First Search implementation in Rust

                    Function to Return a JSON Like Objects Using VBA Collections and Arrays

                    C++11 CLH Lock Implementation