datetime - Accurate Timers in Vanilla JavaScript -
i work creating timers / stopwatches , i've come upon problem.
when using setinterval();
, rather ever accurate. i'm using safari 8, , after 20 seconds, can off +-8 seconds. i'm trying time every 10ms requestframeanimation
won't cut because i'm not animating.
i've come following uses date
object
var runs = 0, speed = 10, timeout = speed, time = math.floor(new date().gettime() / speed); function timer () { runs += 1; console.log(runs, new date().gettime() / speed); if (math.floor(new date().gettime() / speed) > time + 1) { timeout = speed - (math.floor(new date().gettime() / speed) - time); } else if (math.floor(new date().gettime() / speed) < time + 1) { timeout = speed + (time - math.floor(new date().gettime() / speed)); } else { timeout = speed; } time = math.floor(new date().gettime() / speed); settimeout(function () { timer(); //repeat }, timeout); } timer();//starts timer
except can still have 3 runs within 100 ms (.6
, .64
, .68
) third of speed.
i've seen many solutions on how can achieved other languages such node.js, java, c#
, javascript libraries can't seem solve basic problem.
is there i'm missing? what's best way this?
there 2 methods want.
- use background process (web worker).
- track time against timestamp @ beginning, , show difference between , then, instead of incrementing per interval frame.
web worker version
if can support web workers, use run dedicated background process supports type of time frame push you're wanting (increment timer frame frame in interval, without timestamp diff, , keep accurate).
here example, found on webpage:
http://frenticb.com/tricks/simple-timer.php
<div class="header">a simple timer:</div> <div class="timer" id="timer">00:00</div> <div class="buttons"> <button onclick="starttimer()" id="button1">start</button> <button onclick="stoptimer()" id = "button2">stop</button> </div> <script> var w = null; // initialize variable // function start timer function starttimer(){ // first check whether web workers supported if (typeof(worker)!=="undefined"){ // check whether web worker has been created. if not, create new web worker based on javascript file simple-timer.js if (w==null){ w = new worker("simple-timer.js"); } // update timer div output web worker w.onmessage = function (event) { document.getelementbyid("timer").innerhtml = event.data; }; } else { // web workers not supported browser document.getelementbyid("timer").innerhtml = "sorry, browser not support web workers ..."; } } // function stop timer function stoptimer(){ w.terminate(); timerstart = true; w = null; } </script>
and simple.timer.js (note web workers requires url):
var timerstart = true; function mytimer(d0){ // current time var d=(new date()).valueof(); // calculate time difference between , initial time var diff = d-d0; // calculate number of minutes var minutes = math.floor(diff/1000/60); // calculate number of seconds var seconds = math.floor(diff/1000)-minutes*60; var myvar = null; // if number of minutes less 10, add leading "0" minutes = minutes.tostring(); if (minutes.length == 1){ minutes = "0"+minutes; } // if number of seconds less 10, add leading "0" seconds = seconds.tostring(); if (seconds.length == 1){ seconds = "0"+seconds; } // return output web worker postmessage(minutes+":"+seconds); } if (timerstart){ // current time var d0=(new date()).valueof(); // repeat mytimer(d0) every 100 ms myvar=setinterval(function(){mytimer(d0)},100); // timer should not start anymore since has been started timerstart = false; }
non-web worker version
and non-web worker version:
<p id='timer'>0.00</p> <p id='starter-container'> <button type='button' id='starter'>start</button> <button type='button' id='starter-reset'>reset</button> </p> <script> (function oab(){ // keep local. var runs = 0, max_runs = 10000, speed = 10, timeout = speed, start_time = 0, time = 0, num_seconds = (30) * 1000, mark_every = 100, mark_next = time * speed, timer_el = document.getelementbyid('timer'), starter = document.getelementbyid('starter'), reset = document.getelementbyid('starter-reset'); starter.addeventlistener('click', function cl(){ reset_timer(); init_timer(); do_timer(); this.disabled = true; }); reset.addeventlistener('click', function cl(){ runs = max_runs++; }); function init_timer() { start_time = new date().gettime(); time = math.floor(start_time / speed); } function reset_timer() { runs = 0; starter.disabled = false; timer_el.innertext = '0.00'; } function do_timer(){ init_timer(); (function timer () { var c_time = new date().gettime(), time_diff = c_time - start_time, c_secs = 0; runs += 1; c_secs = (math.round(time_diff / 10, 3) / 100).tostring(); if (c_secs.indexof('.') === -1) { c_secs += '.00'; } else if (c_secs.split('.').pop().tostring().length === 1 ) { c_secs += '0'; } timer_el.innertext = c_secs; if (c_time >= mark_next) { console.log( 'mark_next: ' + mark_next, 'mark time: ' + c_time, '(' + (math.floor(c_time * .01) * 100).tostring().substring(10) + ')', 'precision: ' + (mark_next - c_time) + ')' ); mark_next = math.floor((c_time + mark_every) * .01) * 100; } if (math.floor(c_time / speed) > time + 1) { timeout = speed - ((c_time / speed) - time); } else if (math.floor(c_time / speed) < time + 1) { timeout = speed + (time - math.floor(c_time / speed)); } else { timeout = speed; } time = math.floor(new date().gettime() / speed); if (runs >= max_runs || time_diff > num_seconds) { reset_timer(); return; } settimeout(timer, timeout); })(); } })(); </script>