Simple timer, and setinterval - javascript

What's the best way to create a timer in JS?
I've been using this so far:
var sec = 0;
setInterval(function (){sec +=1}, 1000);
I've noticed that, when I need miliseconds, it slows down by a lot. On browser tab changes, it completely stops.
var milisec = 0;
setInterval(function (){milisec +=1}, 1);
I'm looking for a better way to handle this, which will also continue to work when the browser window is changed.

With milliseconds, the resolution of the timer isn't large enough. In most cases the callback won't be called more often than roughly 50 to 250 times per second, even when you set the interval to 1ms. See Timer resolution in browsers (as referred to by Sani Huttunen) for an explanation.
With 1000ms it will work better. But still the timer won't be fired when the tab is inactive, and may be delayed when the cpu is busy or another script is running on your page.
One solution is to not increment a counter, but to test how much time has actually passed since the previous call of the timer. That way, the timing remains accurate, even when the intervals have been delayed or paused inbetween.
This snippet will remember the start date, and on each timer interval, update seconds and milliseconds to the difference between the current time and the start time.
var start = new Date();
var milliseconds = 0;
var seconds = 0;
setInterval(function()
{
var now = new Date();
milliseconds = now.getTime() - start.getTime();
seconds = round(milliseconds / 1000);
}, 1000);
I've set the interval to 1000 again. You might set it shorter, but it will cost more performance.
Related question: How can I make setInterval also work when a tab is inactive in Chrome?

Based on #goleztrol's solution, I've created an alternate solution specifically for my situation (it might not work for everyone).
I just ask the exact time when it's needed with this function, to know the exact miliseconds passed:
var start = new Date();
var msPassed = function() {
var now = new Date();
var ms = now.getTime() - start.getTime();
return ms
}
msPassed(); //returns time passed in ms
I needed to position objects (on creation) depending on how much time passed until their creation, so for my case this is a perfect solution. However, my initial question asks for the perfect timer, and this is not it. Anyway, here it is for future reference.

Related

setInterval timer be more slower when go another chrome tab

I build a countdown timer, so - When I stay in the countdown chrome tab, its work fine.
But when I go to another tab (while the countdown) running, its made it slower.
for example when you start countdown for 30 seconds, and you go to another tab for 10 seconds, when you go back to the countdown tab it will be in 24 seconds - instead of 20 seconds (its just example its very changeable).
stopper = setTimeout(progressCountdown, 1000);
the progressCountdown function its not needed here because its work fine when I in the countdown tab.
There is some option to fix that?
A timeout is not guaranteed to be run after exactly the given delay. In fact, there can be a number of reasons why the delay will be longer than the one you give, for instance if your web page or CPU is overloaded with work, or if it's throttled by the browser to save power and battery (which is what many browsers do with background tabs). MDN has a list with many reasons why these things may occur.
What seems to be your actual problem, however, is that you're using the timeouts to track time itself. This will often be very inaccurate due to the above problems. What you should do instead is to use a clock to keep track of the time spent before the timeout fires:
// performance.now() is a good way of measuring durations
let atStart = performance.now();
function timeoutCallback() {
let now = performance.now();
// milliseconds is accurate now matter how long the timeout actually took
// no matter if in foreground or in background tab
let milliseconds = now - atStart;
// number of seconds left
let secondsLeft = Math.ceil(30 - milliseconds / 1000);
console.log('Countdown: ' + secondsLeft);
if(secondsLeft > 0)
setTimeout(timeoutCallback, 1000);
}
setTimeout(timeoutCallback, 1000);

In Javascript, how to create an accurate timer with milliseconds?

I saw a Javascript milliseconds timer demo HERE
However, I found it very inaccurate because it uses setInterval to increase one ms per time.
Does anyone have ideas about how to implement an accurate milliseconds timer in JS easily? Does it has to be done using Date object?
An accurate timer uses setTimeout() or setInterval() to regularly update a display, but NEVER to count the actual time. Because of Javascript's single threaded nature and event driven system, a timer event may not occur exactly at the right time interval, but a call to Date.now() will always give you the exact current system time.
So, instead, you always use something like Date.now() to get the current time and compare it to some previous time to calculate the actual elapsed time. This will be as accurate as the system clock on the computer is.
For example, here's a working timer display:
var startTime = Date.now();
var interval = setInterval(function() {
var elapsedTime = Date.now() - startTime;
document.getElementById("timer").innerHTML = (elapsedTime / 1000).toFixed(3);
}, 100);
<span id="timer"></span> s
Yes. Using Date will be much more accurate. setInterval will not be triggered 'exactly' each milliseconds.
var startTime, interval;
function start(){
startTime = Date.now();
interval = setInterval(function(){
updateDisplay(Date.now() - startTime);
});
}
function stop(){
clearInterval(interval);
}
function updateDisplay(currentTime){
// do your stuff
}
setInterval could be switchet with
var x = 100; //ms time
var incrementFunction = function(){
//increment the watch/clock code
setTimeout(incrementFunction,x);
};
incrementFunction();
this will make a setTimeout function to be endless called, and set again.
It seems that setTimeout, has bigger priority than setInterval, which could be delayed.

call function on the minute every minute

The code I wrote to call a function on the minute every minute, I think is flawed, as It's good for a while, but tends to lag behind by about 15 seconds for every hour since the page was loaded. To be honest I can't figure out what's causing the lagging, maybe it's the time it takes the functions to execute, small lapses all adding up and accumulating. Is there a way to auto-correct the lapses within the function as it's called. Or maybe someone knows a better method of achieving on the minute function calls. Any help or ideas much appreciated. Thanks.
var now = new Date();
var delay = 60 * 1000; // 1 min in msec
var start = delay - (now.getSeconds()) * 1000 + now.getMilliseconds();
setTimeout(function setTimer() {
onTheMinFunc();
setTimeout(setTimer, delay);
}, start);
First of all, the DOM Timers API does not guarantee accuracy. I quote:
This API does not guarantee that timers will run exactly on schedule. Delays due to CPU load, other tasks, etc, are to be expected.
Second, you have a lag on each round caused by the time onTheMinFunc() is executed (you only set the timeout when it's done).
So, let's say onTheMinFunc takes half a second to execute - you get half a second delay at each minute and it accumulates - after only 10 minutes it'll lag quite a bit. (Note, functions should usually not take more than 15ms to execute anyway to avoid noticeable lag)
Try:
setInterval(onTheMinFunc, delay);
It still won't be very accurate. You can poll on much shorter intervals and keep track of a date variable - but again - no guarantees.
What you probably want is setInterval:
setInterval(onTheMinFunc, delay);
As is, your code using setTimeout means that the time it takes to execute your onTheMinFunc is being added into your delay before the next one is started, so over time, this extra delay will add up.
Using setInterval will be more accurate, since the delay is between calls to execute the function, rather than starting the timer only after the function is finished.
Timers and javascript times aren't very accurate, and I would think the only way to make sure a function is executed every whole minute over time, is to check the seconds every second
setInterval(function() {
if ( new Date().getSeconds() === 0 ) onTheMinFunc();
},1000);
FIDDLE
Here is a slight modification to your code:
function everyMinute(fn) {
arguments[1] && fn();
var now = new Date();
var delay = 60 * 1000 - (now.getSeconds()) * 1000 + now.getMilliseconds();
setTimeout(function(){
everyMinute(fn, true);
}, start);
}
everyMinute(onTheMinFunc);
It recalculates the number of milliseconds to wait till the next minute every time so it is as accurate as possible to the top of the minute.
I think you want something closer to this:
function setNextMinute() {
// figure out how much time remains before the end of the current minute
var d = new Date().getTime()%60000;
//set a timeout to occur when that expires.
setTimeout(function () {
// recalculate a new timeout so that your timer doesn't lag over time.
doWhateverYouWantToHere();
// note that calling doWhateverYouWantToHere() will
// not offset the next minute, since it is recalculated in setNextMinute()
setNextMinute();
},60000-d);
}
setNextMinute();
caveat: I did not thoroughly test this for timing. But it appeared to work for 1 sec intervals and 1 min intervals well enough.
This has the advantage of not recalculating every second, and also not just starting a 60 second timer from whatever the current time is.
The current accepted answer may overkill
Executing if ( new Date().getSeconds() === 0 ) onTheMinFunc(); on each second (and forever) seems to not be a good idea.
I will not benchmark it against the following propositions, it's not necessary.
Clues
Use whatever logic is necessary to calculate the start moment.
On the start moment
Use setInterval for remaning executions
Execute the first call
Note setInterval is called ASAP to avoid that time lapses.
If you want that new Date().getSeconds() === 0:
var id = setInterval(function() {
if ( new Date().getSeconds() === 0 ) {
setInterval(onTheMinFunc, delay);
onTheMinFunc();
clearInterval(id);
}
},1000);
Alternatively, you could use your own logic:
var now = new Date();
var delay = 60 * 1000; // 1 min in msec
var start = delay - (now.getSeconds()) * 1000 + now.getMilliseconds();
setTimeout(function() {
setInterval(onTheMinFunc, delay);
onTheMinFunc();
}, start);
Please check both examples working on jsfiddle
The second (Example B) seems more accurate.

Javascript countdown is going slow?

I'm trying to make a countdown that is counting down in milliseconds; however, the countdown actually takes much longer than 7 seconds. Any idea as to why?
function countDown(time){
var i = 0;
var interval = setInterval(function(){
i++;
if(i > time){
clearInterval(interval);
}else{
//mining
$('#mining_time').text($('#mining_time').text()-1);
}
}, 1);
}
And I can confirm the varible time passed to the function is correctly set to 7000.
For a mostly-accurate countdown, use setTimeout().
setTimeout(fn, 7e3);
If you absolutely must have it as close to 7 seconds as possible, use a tight poll (requestAnimationFrame()) and look at difference between the time of start and current poll.
var startTime = Date.now();
requestAnimationFrame(function me() {
var deltaTime = Date.now() - startTime;
if (deltaTime >= 7e3) {
fn();
} else {
requestAnimationFrame(me);
}
});
Poly-fill as required.
the most precise way to run something after 7 seconds - is to use setTimeout with 7000 ms interval
a. there is no browser that guarantees an interval to run with 1ms resolution. In the best case it would be 7-10ms
b. there is only one thread in js, so the tasks are queued. It means that the next run will be scheduled to only after the current run is finished.
Some useful reading: http://ejohn.org/blog/how-javascript-timers-work/
No browser will take 1 as parameter for setInterval. Off the top of my head the minimum is 4 ms.
For an accurate result, get the current time, add 7000 ms, and poll (using setInterval or setTimeout) until you reach that new time.
A quick Web search returned this article that provides an example.
[Update] the value of 4 ms is mentioned on this MDN page.

ajax/JS timed countdown?

Let me explain what I'm trying to do.
I want to make a simple box which counts down numbers at intervals I specify.
For example, I'd like to set it to start at 150, and then I want to set it to drop by 15 every 30 seconds.
Is this possible with AJAX/Javascript? If so, could someone point me in the right direction?
Would really appreciate any help on this script, been Googling for hours now! :(
Cheers
Kieran
Have a look at the setTimeout or setInterval methods, they allow you to execute a function after a specified number of milliseconds (1000ms = 1second). Use that, to call a function that keeps dropping the number and writes it to a HTML element to the user can see it.
this isn't tested, but i hope it shows you the way to go.
var start = 150;
var drop = 15;
var interval = 30;
function countdown(){
document.getElementById('mybox').innerHTML = start;
start-=drop;
window.setTimeout("countdown",interval*1000);
}
countdown();
You may use jQuery to do that, see http://keith-wood.name/countdown.html -> tab Callbacks
Keep in mind that 30 seconds in my browser are not necessarily equal to 30 seconds in your browser. It depends on the workload of the browser.
The time difference is minor for a short time but can increase over a long time. The times will drift apart. If the times must not be equal (or nearly equal) between two visitors than such simple solution should be fine.
We had once a problem to introduce a live clock / countdown in one of our projects. We build a script with javascript, ajax and PHP for clock synchronisation (server time was timeserver).
You should use setInterval / clearInterval which is made for this kind of tasks:
function cooldown(element, start, stop, step, delay) {
var current = start;
element.innerHTML = current;
var timer = setInterval(function () {
current -= step;
if(current < stop) current=stop;
element.innerHTML = current;
if(current == stop) clearInterval(timer);
}, delay*1000);
}
Demonstrated here : http://jsfiddle.net/PCMHn/

Categories