JavaScript: Is this timer reliable? - javascript

Today I was introduced to the world of Web Workers in JavaScript. This made me rethink about timers. I used to program timers the ugly way, like this.
var time = -1;
function timerTick()
{
time++;
setTimeout("timerTick()",1000);
$("#timeI").html(time);
}
I know this could be improved by saving the date when you start the timer, but I've never been a fan of that.
Now I came up with a method using Web Workers, I did a little benchmark and found it much more reliable. Since I am not an expert on JavaScript I would like to know if this function works correct or what problems it might have thanks in advance.
My JavaScript code (please note I use JQuery):
$(function() {
//-- Timer using web worker.
var worker = new Worker('scripts/task.js'); //External script
worker.onmessage = function(event) { //Method called by external script
$("#timeR").html(event.data)
};
};
The external script ('scripts/task.js'):
var time = -1;
function timerTick()
{
time++;
setTimeout("timerTick()",1000);
postMessage(time);
}
timerTick();
You can also view a live demo on my website.

If you're trying to reliably display seconds ticking by, then the ONLY reliable way to do that is to get the current time at the start and use the timer ONLY for updating the screen. On each tick, you get the current time, compute the actual elapsed seconds and display that. Neither setTimeout() nor setInterval() are guaranteed or can be used for accurately counting time.
You can do it like this:
var start = +(new Date);
setInterval(function() {
var now = +(new Date);
document.getElementById("time").innerHTML = Math.round((now - start)/1000);
}, 1000);
If the browser gets busy and timers are erratically spaced, you may get a slightly irregular update on screen, but the elapsed time will remain accurate when the screen is updated. Your method is susceptible to accumulating error in the elapsed time.
You can see this work here: http://jsfiddle.net/jfriend00/Gfwze/

The most accurate timer would be a comparison of two time stamps. You could increase the precision of your timer by updating more frequently (such as every 100ms). I prefer using setInterval() over setTimeout().

Related

The javascript timing resolution in my browsers seems to be ~8ms. How can I increase it? [duplicate]

Something that has always bugged me is how unpredictable the setTimeout() method in Javascript is.
In my experience, the timer is horribly inaccurate in a lot of situations. By inaccurate, I mean the actual delay time seems to vary by 250-500ms more or less. Although this isn't a huge amount of time, when using it to hide/show UI elements the time can be visibly noticeable.
Are there any tricks that can be done to ensure that setTimeout() performs accurately (without resorting to an external API) or is this a lost cause?
Are there any tricks that can be done
to ensure that setTimeout() performs
accurately (without resorting to an
external API) or is this a lost cause?
No and no. You're not going to get anything close to a perfectly accurate timer with setTimeout() - browsers aren't set up for that. However, you don't need to rely on it for timing things either. Most animation libraries figured this out years ago: you set up a callback with setTimeout(), but determine what needs to be done based on the value of (new Date()).milliseconds (or equivalent). This allows you to take advantage of more reliable timer support in newer browsers, while still behaving appropriately on older browsers.
It also allows you to avoid using too many timers! This is important: each timer is a callback. Each callback executes JS code. While JS code is executing, browser events - including other callbacks - are delayed or dropped. When the callback finishes, additional callbacks must compete with other browser events for a chance to execute. Therefore, one timer that handles all pending tasks for that interval will perform better than two timers with coinciding intervals, and (for short timeouts) better than two timers with overlapping timeouts!
Summary: stop using setTimeout() to implement "one timer / one task" designs, and use the real-time clock to smooth out UI actions.
.
REF; http://www.sitepoint.com/creating-accurate-timers-in-javascript/
This site bailed me out on a major scale.
You can use the system clock to compensate for timer inaccuracy. If you run a timing function as a series of setTimeout calls — each instance calling the next — then all you have to do to keep it accurate is work out exactly how inaccurate it is, and subtract that difference from the next iteration:
var start = new Date().getTime(),
time = 0,
elapsed = '0.0';
function instance()
{
time += 100;
elapsed = Math.floor(time / 100) / 10;
if(Math.round(elapsed) == elapsed) { elapsed += '.0'; }
document.title = elapsed;
var diff = (new Date().getTime() - start) - time;
window.setTimeout(instance, (100 - diff));
}
window.setTimeout(instance, 100);
This method will minimize drift and reduce the inaccuracies by more than 90%.
It fixed my issues, hope it helps
I had a similar problem not long ago and came up with an approach which combines requestAnimationFrame with performance.now() which works very effectively.
Im now able to make timers accurate to approx 12 decimal places:
window.performance = window.performance || {};
performance.now = (function() {
return performance.now ||
performance.mozNow ||
performance.msNow ||
performance.oNow ||
performance.webkitNow ||
function() {
//Doh! Crap browser!
return new Date().getTime();
};
})();
http://jsfiddle.net/CGWGreen/9pg9L/
If you need to get an accurate callback on a given interval, this gist may help you:
https://gist.github.com/1185904
function interval(duration, fn){
var _this = this
this.baseline = undefined
this.run = function(){
if(_this.baseline === undefined){
_this.baseline = new Date().getTime()
}
fn()
var end = new Date().getTime()
_this.baseline += duration
var nextTick = duration - (end - _this.baseline)
if(nextTick<0){
nextTick = 0
}
_this.timer = setTimeout(function(){
_this.run(end)
}, nextTick)
}
this.stop = function(){
clearTimeout(_this.timer)
}
}
shog9's answer is pretty much what I'd say, although I'd add the following about UI animation/events:
If you've got a box that's supposed to slide onto the screen, expand downwards, then fade in its contents, don't try to make all three events separate with delays timed to make them fire one after another - use callbacks, so once the first event is done sliding it calls the expander, once that's done it calls the fader. jQuery can do it easily, and I'm sure other libraries can as well.
If you're using setTimeout() to yield quickly to the browser so it's UI thread can catch up with any tasks it needs to do (such as updating a tab, or to not show the Long Running Script dialog), there is a new API called Efficient Script Yielding, aka, setImmediate() that may work a bit better for you.
setImmediate() operates very similarly to setTimeout(), yet it may run immediately if the browser has nothing else to do. In many situations where you are using setTimeout(..., 16) or setTimeout(..., 4) or setTimeout(..., 0) (i.e. you want the browser to run any outstanding UI thread tasks and not show a Long Running Script dialog), you can simply replace your setTimeout() with setImmediate(), dropping the second (millisecond) argument.
The difference with setImmediate() is that it is basically a yield; if the browser has sometime to do on the UI thread (e.g., update a tab), it will do so before returning to your callback. However, if the browser is already all caught up with its work, the callback specified in setImmediate() will essentially run without delay.
Unfortunately it is only currently supported in IE9+, as there is some push back from the other browser vendors.
There is a good polyfill available though, if you want to use it and hope the other browsers implement it at some point.
If you are using setTimeout() for animation, requestAnimationFrame is your best bet as your code will run in-sync with the monitor's refresh rate.
If you are using setTimeout() on a slower cadence, e.g. once every 300 milliseconds, you could use a solution similar to what user1213320 suggests, where you monitor how long it was from the last timestamp your timer ran and compensate for any delay. One improvement is that you could use the new High Resolution Time interface (aka window.performance.now()) instead of Date.now() to get greater-than-millisecond resolution for the current time.
You need to "creep up" on the target time. Some trial and error will be necessary but in essence.
Set a timeout to complete arround 100ms before the required time
make the timeout handler function like this:
calculate_remaining_time
if remaining_time > 20ms // maybe as much as 50
re-queue the handler for 10ms time
else
{
while( remaining_time > 0 ) calculate_remaining_time;
do_your_thing();
re-queue the handler for 100ms before the next required time
}
But your while loop can still get interrupted by other processes so it's still not perfect.
Here's an example demoing Shog9's suggestion. This fills a jquery progress bar smoothly over 6 seconds, then redirects to a different page once it's filled:
var TOTAL_SEC = 6;
var FRAMES_PER_SEC = 60;
var percent = 0;
var startTime = new Date().getTime();
setTimeout(updateProgress, 1000 / FRAMES_PER_SEC);
function updateProgress() {
var currentTime = new Date().getTime();
// 1000 to convert to milliseconds, and 100 to convert to percentage
percent = (currentTime - startTime) / (TOTAL_SEC * 1000) * 100;
$("#progressbar").progressbar({ value: percent });
if (percent >= 100) {
window.location = "newLocation.html";
} else {
setTimeout(updateProgress, 1000 / FRAMES_PER_SEC);
}
}
This is a timer I made for a music project of mine which does this thing. Timer that is accurate on all devices.
var Timer = function(){
var framebuffer = 0,
var msSinceInitialized = 0,
var timer = this;
var timeAtLastInterval = new Date().getTime();
setInterval(function(){
var frametime = new Date().getTime();
var timeElapsed = frametime - timeAtLastInterval;
msSinceInitialized += timeElapsed;
timeAtLastInterval = frametime;
},1);
this.setInterval = function(callback,timeout,arguments) {
var timeStarted = msSinceInitialized;
var interval = setInterval(function(){
var totaltimepassed = msSinceInitialized - timeStarted;
if (totaltimepassed >= timeout) {
callback(arguments);
timeStarted = msSinceInitialized;
}
},1);
return interval;
}
}
var timer = new Timer();
timer.setInterval(function(){console.log("This timer will not drift."),1000}
Hate to say it, but I don't think there is a way to alleviate this. I do think that it depends on the client system, though, so a faster javascript engine or machine may make it slightly more accurate.
To my experience it is lost effort, even as the smallest reasonable amount of time I ever recognized js act in is around 32-33 ms. ...
There is definitely a limitation here. To give you some perspective, the Chrome browser Google just released is fast enough that it can execute setTimeout(function() {}, 0) in 15-20 ms whereas older Javascript engines took hundreds of milliseconds to execute that function. Although setTimeout uses milliseconds, no javascript virtual machine at this point in time can execute code with that precision.
Dan, from my experience (that includes implementation of SMIL2.1 language in JavaScript, where time management is in subject) I can assure you that you actually never need high precision of setTimeout or setInterval.
What does however matter is the order in which setTimeout/setInterval gets executed when queued - and that always works perfectly.
JavaScript timeouts have a defacto limit of 10-15ms (I'm not sure what you're doing to get 200ms, unless you're doing 185ms of actual js execution). This is due to windows having a standard timer resolution of 15ms, the only way to do better is to use Windows' higher resolution timers which is a system wide setting so can screw with other applications on the system and also chews battery life (Chrome has a bug from Intel on this issue).
The defacto standard of 10-15ms is due to people using 0ms timeouts on websites but then coding in a way that assumes that assumes a 10-15ms timeout (eg. js games which assume 60fps but ask 0ms/frame with no delta logic so the game/site/animation goes a few orders of magnitude faster than intended). To account for that, even on platforms that don't have windows' timer problems, the browsers limit timer resolution to 10ms.
Here are what I use. Since it's JavaScript, I will post both my Frontend and node.js solutions:
For both, I use the same decimal rounding function that I highly recommend you keep at arms length because reasons:
const round = (places, number) => +(Math.round(number + `e+${places}`) + `e-${places}`)
places - Number of decimal places at which to round, this should be safe and should avoid any issues with floats (some numbers like 1.0000000000005~ can be problematic). I Spent time researching the best way to round decimals provided by high-resolution timers converted to milliseconds.
that + symbol - It is a unary operator that converts an operand into a number, virtually identical to Number()
Browser
const start = performance.now()
// I wonder how long this comment takes to parse
const end = performance.now()
const result = (end - start) + ' ms'
const adjusted = round(2, result) // see above rounding function
node.js
// Start timer
const startTimer = () => process.hrtime()
// End timer
const endTimer = (time) => {
const diff = process.hrtime(time)
const NS_PER_SEC = 1e9
const result = (diff[0] * NS_PER_SEC + diff[1])
const elapsed = Math.round((result * 0.0000010))
return elapsed
}
// This end timer converts the number from nanoseconds into milliseconds;
// you can find the nanosecond version if you need some seriously high-resolution timers.
const start = startTimer()
// I wonder how long this comment takes to parse
const end = endTimer(start)
console.log(end + ' ms')
You could consider using the html5 webaudio clock which uses the system time for better accuracy

Stopwatch not working, it's going way too faster

As I was looking for a simple Stopwatch implementation in JS, I found this code http://codepen.io/_Billy_Brown/pen/dbJeh/
The problem is that it's not working fine, the clock go way too fast. I got 30 seconds on the screen when i got only 23 seconds on my watch.
And I don't understand why. The timer function is called every millisecond and should be updating the time correctly.
setInterval(this.timer, 1);
Is the problem coming from the browser or from the JS code.
Thanks in advance.
The timers in Javascript doesn't have millisecond precision.
There is a minimum time for the interval, which differs depending on the browser and browser version. Typical minimums are 4 ms for recent browsers and 10 ms for a little older browsers.
Also, you can't rely on the callback being called at exact the time that you specify. Javascript is single threaded, which means that if some other code is running when the timer triggers a tick, it has to wait until that other code finishes.
In fact the code you gave is imitating time flow, but it is not synchronized with system time.
Every millisecond it just invokes the function this.time, which performs recounting of millis, seconds and so on
without getting native system time, but just adding 1 to variable representing "imaginary milliseconds".
So we can say that resulting pseudo-time you see depends on your CPU, browser and who knows what else.
On our modern fantastically fast computers the body of this.time function is being executed faster than millisecond (wondering what would happen on Pentium 2 with IE5 on board).
Anyhow there is no chance for the this.time to be executed exactly in particular fixed period on all computers and browsers.
The simplest correct way to show the time passed since startpoint according to the system time is:
<body>
<script>
var startTime = new Date() // assume this initialization to be start point
function getTimeSinceStart()
{
var millisSinceStart = new Date() - startTime
, millis = millisSinceStart % 1000
, seconds = Math.floor(millisSinceStart / 1000)
return [seconds, millis].join( ':' )
}
(function loop()
{
document.title = getTimeSinceStart() // look on the top of page
setTimeout( loop, 10 )
}())
</script>
</body>
P.S. What #Guffa says in his answer is correct (generally for js in browsers), but in this case it does not matter and not affect the problem

Getting certain frequency with setInterval method

In a javascript code I develop, some function should be called every 1 second. But to be sure that this operation takes place every 1 second, the following code is utilized:
setInterval(doIt, 500);
function doIt() {
var now = (new Date()).getTime();
if(lastUpdate + 1000 >= now) {
/// code...
lastUpdate = now;
}
}
As far as I know setInterval(doIt, 1000) doesn't always mean that it's called every one second.
Is the above solution is a valid one? If not, what do you recommend?
You could use setTimeout instead of setInterval, and make dynamic adjustments each time your function is called. The idea is to set the timeout for a number of milliseconds sufficient to carry you to the next second boundary.
function timeoutFunc() {
// do interesting things
var time = new Date().getTime();
setTimeout(timeoutFunc, 1000 - time % 1000);
}
You'd start it off with:
setTimeout(timeoutFunc, 1000 - new Date().getTime() % 1000);
Synchronizing with the server seems like a bad idea, because you have no way of knowing whether the client clock is synchronized to anything (like the NTP server network). If it's not, then you server synchronizations are going to make things look wrong at the client, because the client clock will always be what seems right.
well setInterval IS defined in milliseconds. so it means its called every X millisdconds.
however the system can freeze or something like that!
but theres no practical better solution, you approach is fine.
if you really have an extensive javascript client application the results could stretch a little bit.
a possible solution for that is to get the system time and have a counter in your function. then ever X executions you align with the system clock, calculate how many function calls you should have until now and speed up the interval or slow it down.
this is as far as you can get to perfection. but it will be only a matter of milliseconds and probably not worth the effort.
may i ask what you are developing?

Will setInterval drift?

This is a pretty simple question really. If I use setInterval(something, 1000), can I be completely sure that after, say, 31 days it will have triggered "something" exactly 60*60*24*31 times? Or is there any risk for so called drifting?
Short answer: No, you can't be sure. Yes, it can drift.
Long answer: John Resig on the Accuracy of JavaScript Time and How JavaScript Timers Work.
From the second article:
In order to understand how the timers work internally there's one important concept that needs to be explored: timer delay is not guaranteed. Since all JavaScript in a browser executes on a single thread asynchronous events (such as mouse clicks and timers) are only run when there's been an opening in the execution.
Both articles (and anything on that site) is great reading, so have at it.
Here's a benchmark you can run in Firefox:
var start = +new Date();
var count = 0;
setInterval(function () {
console.log((new Date() - start) % 1000,
++count,
Math.round((new Date() - start)/1000))
}, 1000);
First value should be as close to 0 or 1000 as possible (any other value shows how "off the spot" the timing of the trigger was.) Second value is number of times the code has been triggered, and third value is how many times the could should have been triggered. You'll note that if you hog down your CPU it can get quite off the spot, but it seems to correct itself. Try to run it for a longer period of time and see how it handles.
(Sorry about my bad english)
I had same problem about counting down function, i writed a function countdown() and loop with setInterval but its drifting 1-3 milliseconds per loop. Then i write a function that controls is there any drifting and fixed it.
It controls with real minute and second only. Here it is. Its works fine to me, i hope it will help you too.
$.syncInterval(functionname,interval,controlinterval)
example:
countdown(){ some code };
$.syncInterval(countdown,1000,60);
it says run countdown function every 1000 milliseconds and check it every 60 seconds
here is the code:
$.syncInterval = function (func,interval,control) {
var
now=new Date();
realMinute=now.getMinutes(),
realSecond=now.getSeconds(),
nowSecond=realSecond,
nowMinute=realMinute,
minuteError=0,
countingVar=1,
totalDiff=0;
var loopthat = setInterval(function(){
if (nowSecond==0) {
nowMinute++;
nowMinute=nowMinute%60;
};
if (countingVar==0){
now=new Date();
realSecond=now.getSeconds();
realMinute=now.getMinutes();
totalDiff=((realMinute*60)+(realSecond))-((nowMinute*60)+(nowSecond));
if(totalDiff>0){
for (i=1;i<=totalDiff;i++) {
func();
nowSecond++;
countingVar++;
};
} else if (totalDiff==0){
func();
nowSecond++;
countingVar++;
} else if (totalDiff<0) {
};
} else {
func();
nowSecond++;
countingVar++;
};
countingVar=countingVar%control;
nowSecond=nowSecond%60;
},interval);
};

Counting down for x to 0 in Javascript?

I have from the backend a time on the format 00:12:54 and I display it to the screen. But, I would like to have this time to continue to go down. I have though to create a variable in javascript that will old the time and with setTimeout to loop to display with document.getElementById the new value. I think it can be problematic if I have many time to go down in same time. I might require an array?
How would you do that? If I have no other suggestion, I will try my way, but I am curious to know if it does have a more secure way to do it.
Do you know jQuery Framework? It's a Javascript framework that have a lot of utilities methods and functions that let you do Javascript stuff more easily.
Here is a count down plugin (haven't tested it).
I suggest you to download JQuery than download the plugin . Check the sample of code from the "relative" tab on the website. You can have something like :
$('#until2d4h').countdown({until: '+12M +54S'});
*The only drawback with what I suggest you is that you will require 2 .js to be added. Try to add them only when needed and you will be find.
General algorithm:
Read time from server.
Read the current time.
Call a function.
In your function, read the current time, get the delta from the initial time you read in step 2.
Subtract the delta from the initial time you read from the server in step 1 and display the remainder.
The function should call window.setTimeout to call itself in 1000ms (or adjust according to time elapsed within the function), if you want to continue counting down.
Here's a rough cut:
window.onload = function () {
var countdown_start_in_ms = 6000; // from server
function tick() {
var now = new Date().getTime();
var disp = start - now;
if (disp < 0) {
disp = 0;
}
var el = document.getElementById("countdown");
el.innerHTML =
// quick hack to format time
/(\d\d:\d\d:\d\d) ...$/.exec(new Date(disp).toUTCString())[1];
if (disp > 1000) {
var elapsed = new Date().getTime() - now;
window.setTimeout(tick, 1000 - elapsed);
} else {
// stop countdown and set color to light grey
el.style.color = "#ccc";
}
}
var start = new Date().getTime() + countdown_start_in_ms;
tick();
}
You won't like the taste of this one, but it'll do you good:
Google for 'javascript timer' and get your hands dirty reading through the various examples and tutorials returned by that search.
You'll learn a lot more than just how to write a count-down timer. :-)
Good luck!
Take a look at Grab hands and set your own time. and inspect its code. While it is written with Dojo, the "clock" part is in plain JavaScript. In your case the only difference is how to advance the counter — decrease rather than increase it.

Categories

Resources