This question already has answers here:
How do browsers pause/change Javascript when tab or window is not active?
(3 answers)
Closed 9 years ago.
Here's the simplest code for reproducing I could think of:
ms = 30; // 1000 ?
num = 1;
function test()
{
num+=ms;
document.getElementById('Submit').value = num; // Using native Javascript on purpose
if (num < 4000)
window.setTimeout(test, ms);
}
test()
I set the ms (milliseconds between iterations) to 30, ran the script and moved to different tab on the browser.
Then I wait for about 10 seconds (the script should finish within 4 seconds) and came back to the tab.
If I used Firefox I saw that the script has not finished, and the numbers are still running (resuming from where I left them, I guess).
Which is annoying enough,
But if I changed ms to 1000 and repeat the above steps, when I come back to the tab I saw the script has indeed already finished.
(The script should still take 4 seconds to finish).
Namely, sometimes Firefox runs window.setTimeout even if the window is out of focus, and sometimes it doesn't. Possibly depending on the duration
On the other hand, this is not happening with Internet Explorer.
It keeps running the script even if the tab is not focused. No matter how I set the ms.
Is that due to some performance considerations of Firefox?
What exactly is happening?
How come such a basic thing is in consistent between browsers,
nowadays?
OR, am I working wrong? Is it a weird way for coding?
I'm just trying repeatedly change the DOM, in a delayed fashion, without using setInterval (because I'm changing the interval it self on the go).
And most important, how should I regard this?
I can't assume my user won't leave the tab.
I want to allow my user to leave the page for as mush as one might like.
If one leaves and come back after half an hour, he/she will see irrelevant animations on the page, still running.
Some of these animations are seen by all the users connecting to the page.
There is no need they will be synchronized in resolution of milliseconds, but I can't start them only when the user put the tab/window in focus.
I'm using Firefox 25.0.1, and IE 11. (Windows 7)
Most modern browsers (especially on mobile devices) suspend execution of scripts in tabs that are out of focus to save CPU cycles (for instance, this is why requestAnimationFrame was brought to life). In the case of timeouts, shorter intervals are actually changed to a different / higher value as the browser vendor sees fit.
What you can do to overcome this (if you really must know the interval between successive executions) is to set a timestamp when the timeout is activated, and compare it with the timestamp when the timeout handler is actually executed. Note that when you're animating it's best to calculate properties of the animated Objects by taking other application variables into account, rather than rely on the amount of calls a particular handler has had.
You could also attach listeners to the window for "(un)focus" Events to know when the user has "come back" to your application. In this event handler you can verify whether a timeout was pending and execute its callback manually, if you must do so.
see the difference: http://jsfiddle.net/qN6eB/
ms = 30; // 1000 ?
num = 1;
start = new Date();
function test()
{
num+=ms;
document.getElementById('Submit').value = num;
if (num < 4000)
window.setTimeout(test, ms);
else
document.getElementById('Time').value = new Date() - start;
}
test()
ms2 = 30; // 1000 ?
num2 = 1;
start2 = new Date();
dueTo = new Date(+new Date()+4000);
function test2()
{
num2+=ms2;
document.getElementById('Submit2').value = num2;
if (new Date() < dueTo)
window.setTimeout(test2, ms2);
else
document.getElementById('Time2').value = new Date() - start2;
}
test2()
setTimeout is not precise for timing. Because the timer doesn't interrupt the process, I'll wait for idle time. I don't know how the browsers are managing it, but an inactive tab probably has a lower priority.
I can think about two solutions :
- Try setInterval (I'm not sure if this will solve your problem or not)
- Instead of incrementing a variable, use a Date object, containing the time at the beginning, and compare it with the current time when the function is executed.
var beginTime = (new Date()).getTime();
var intervalId = setInterval(function() {
var timePassed = (new Date()).getTime() - beginTime;
document.getElementById('Submit').value = timePassed;
if(timePassed >= 4000) {
clearInterval(intervalId);
}
}, 30);
Related
I am implementing a live clock in front-end of a large application. For that I have came up with following approach -
JavaScript
var span = document.getElementById('span');
function time() {
var d = new Date();
var s = d.getSeconds();
var m = d.getMinutes();
var h = d.getHours();
span.textContent = h + ":" + m + ":" + s;
}
setInterval(time, 1000);
HTML
<span id="span"></span>
This approach works perfectly fine in isolation, but when this code is integrated in the application which is having several events and function calls, the clock starts lagging by few minutes after say couple of hours until the page is refreshed.
I think this delay is because of setInterval being a web (browser) API and is handled asynchronously it may not execute exactly after 1 second as written in the code every time, if the call stack is not empty after 1 second from time being setInterval is registered due to other function calls/ events present in the call stack of event loop.
So if the page is not refreshed for long time the delay continues to grow. Also the application is written in Angular which is a Single Page application where the page never reloads on navigation because of routing until the page is forcefully refreshed.
So how to build a precise clock in JavaScript which will never delay when integrated in a large application?
Update: Thanks everyone for the response. I think some of you are right, I may be missing some of the details. Actually I was implementing this few days back at work, but have to left this due to some reason and lost track of it. But there was some delay issue for sure working with Date and Timers. Suddenly now this came to my mind and thought asking it here. Extremely sorry for not providing concrete details.
Still I will try to recollect the details and update the question accordingly if possible.
the clock starts lagging by few minutes after say couple of hours until the page is refreshed.
Thats impossible with the code you've shown, new Date should return the correct time, no matter how often you reflect its value to the page.
So if the page is not refreshed for long time the delay continues to grow.
Most browsers today will adjust the timers slightly, so that they are quite accurate on average (e.g. if one timer gets called to late by 1ms, the next will be called 1ms earlier), therefore you can only cause a drift over a longer time if you will leave the page, which will pause the timer. That still shouldn't affect new Date though.
Have a look at the Chromium source
Timers in web browsers get dialled back when the page doesn't have focus. You can't change or prevent that. You're already doing the primary thing that's important: Using the current time to update the clock, so that even if your time function isn't called for three seconds, when it runs it updates with the then-current time, skipping over the intermediate values. (You often see people assuming the timer will run at exactly 1000ms intervals and just adding to the seconds value rather than using the current time, which is incorrect.)
If I were doing this, I'd probably decrease the interval (run the callback more often) and use a chained series of setTimeout rather than a single setInterval, not least because different browsers have historically handled setInterval in different ways.
So for instance:
function time() {
var d = new Date();
var s = d.getSeconds();
var m = d.getMinutes();
var h = d.getHours();
span.textContent = h + ":" + m + ":" + s;
setTimeout(time, 250);
}
time();
But if the page is inactive, the clock will get out of date, because the browser will either completely suspend timer execution or at least markedly dial it back. When the page becomes active again, though, hopefully it'll correct itself after no more than 250ms.
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.
I build a web app and I use setInterval with 500ms timer for some clock.
When the window is active the clock runs perfect, I use that:
var tempTimer = 0;
setInterval(function () {
goTimer()
}, 500);
function goTimer() {
tempTimer++;
$("#timer").val(tempTimer);
}
But the problem is when the window/tab becomes inactive - the interval is changed to 1000ms!
When i focus the window/tab again, it changes back to 500ms.
check this out: http://jsfiddle.net/4Jw37/
Thanks a bunch.
Yes, this behavior is intentional.
See the MDN article:
In (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2) and Chrome 11,
timeouts are clamped to firing no more often than once per second
(1000ms) in inactive tabs; see bug 633421 for more information about
this in Mozilla or crbug.com/66078 for details about this in Chrome.
And the spec says:
Note: This API does not guarantee that timers will run exactly on
schedule. Delays due to CPU load, other tasks, etc, are to be
expected.
You seem to want a timer that increments every half second. You can do that much more accurately by keeping track of the total time since you started and doing some math.
var tempTimer = 0;
var startedTimer = Date.now();
setInterval(goTimer, 250); // a little more often in case of drift
function goTimer() {
tempTimer = Math.floor((Date.now() - startedTimer) / 500);
$("#timer").val(tempTimer);
}
See this work here: http://jsfiddle.net/4Jw37/2/
So this does update every half second, but it doesn't need to. If it skips a few beats it will fix itself the next time it fires because it's recalculating each time based on the time since it started tracking. The number of times the function runs is now has no effect on the value of the counter.
So put in another way, do not ask how many times you incremented the count. Instead ask how many half second segments have passed since you started.
For time interval, Browsers may not behave similar for both active and inactive window.
What you can do, When you are setting the time interval you can save the timestamp(initial time). On window.onfocus, take on there timestamp (now) find the difference between initial time and now and use that update the tempTimer.
What’s the best way to get monotonically increasing time in JavaScript? I’m hoping for something like Java’s System.nanoTime().
Date() obviously won’t work, as it’s affected by system time changes.
In other words, what I would like is for a <= b, always:
a = myIncreasingTime.getMilliseconds();
...
// some time later, maybe seconds, maybe days
b = myIncreasingTime.getMilliseconds();
At best, even when using the UTC functions in Date(), it will return what it believes is the correct time, but if someone sets the time backward, the next call to Date() can return a lesser value. System.nanoTime() does not suffer from this limitation (at least not until the system is rebooted).
Modification: [2012-02-26: not intended to affect the original question, which has a bounty]
I am not interested knowing the “wall time”, I’m interested in knowing elapsed time with some accuracy, which Date() cannot possibly provide.
You could use window.performance.now() - since Firefox 15, and window.performance.webkitNow() - Chrome 20]
var a = window.performance.now();
//...
var delay = window.performance.now() - a;
You could wrap Date() or Date.now() so as to force it to be monotonic (but inaccurate). Sketch, untested:
var offset = 0;
var seen = 0;
function time() {
var t = Date.now();
if (t < seen) {
offset += (seen - t);
}
seen = t;
return t + offset;
}
If the system clock is set back at a given moment, then it will appear that no time has passed (and an elapsed time containing that interval will be incorrect), but you will at least not have negative deltas. If there are no set-backs then this returns the same value as Date.now().
This might be a suitable solution if you're writing a game simulation loop, for example, where time() is called extremely frequently — the maximum error is the number of set-backs times the interval between calls. If your application doesn't naturally do that, you could explicitly call it on a setInterval, say (assuming that isn't hosed by the system clock), to keep your accuracy at the cost of some CPU time.
It is also possible that the clock will be set forward, which does not prevent monotonicity but might have equally undesirable effects (e.g. a game spending too long trying to catch up its simulation at once). However, this is not especially distinguishable from the machine having been asleep for some time. If such a protection is desired, it just means changing the condition next to the existing one, with a constant threshold for acceptable progress:
if (t > seen + leapForwardMaximum) {
offset += (seen - t) + leapForwardMaximum;
}
I would suggest that leapForwardMaximum should be set to more than 1000 ms because, for example, Chrome (if I recall correctly) throttles timers in background tabs to fire not more than once per second.
Javascript itself does not have any functionality to access the nanoTime. You might load a java-applet to aqcuire that information, like benchmark.js has done. Maybe #mathias can shed some light on what they did there…
Firefox provides "delay" argument for setTimeout...
this is the one of ways to implement monotonically increased time counter.
var time = 0;
setTimeout(function x(actualLateness) {
setTimeout(x, 0);
time += actualLateness;
}, 0);
a question. If i use setInterval in this manner:
setInterval('doSome();',60000);
am i safe that the doSome() function is triggered every 60 seconds, even if I change the tab in a browser?
Passing a string to setInterval is fine, and is one of two ways to use setInterval, the other is passing a function pointer. It is not wrong in any way like the other answers state, but it is not as efficient (as the code must be reparsed) nor is it necessary for your purpose. Both
setInterval('doSome();', 60000); // this runs doSome from the global scope
// in the global scope
and
setInterval(doSome, 60000); // this runs doSome from the local scope
// in the global scope
are correct, though they have a slightly different meaning. If doSome is local to some non-global scope, calling the latter from within the same scope will run the local doSome at 60000ms intervals. Calling the former code will always look for doSome in the global scope, and will fail if there is no doSome function in the global scope.
The function will reliably be triggered, regardless of tab focus, at intervals of at least 60000ms, but usually slightly more due to overheads and delays.
All browsers clamp the interval value to at least a certain value to avoid intervals being too frequent (I think it's a minimum of 10ms or 4ms or something, I can't exactly remember).
Note that some browsers (the upcoming Firefox 5 is one, but there are probably others that I don't know of) further clamp setInterval drastically to e.g. 1000ms if the tab is not focused. (Reference)
No, the interval cannot execute until the event loop is cleared, so if you do for instance setInterval(func, 1000); for(;;) then the interval will never run. If other browsers tabs run in the same thread (as they do everywhere(?) except for in chrome, then the same applies if those tabs clog the event loop.)
But for an interval as large as 60000 it is at least very likely that the func will be called in reasonable time. But no guarantees.
If the tab with the setInterval() function remains open, then yes the function will be executed every 60 seconds, even if you switch to or open other tabs.
Yeah it works on an example I just created.
http://jsfiddle.net/5BAkx/
Yes, the browser's focus is irrelevant.
However, you should not use a string argument to setInterval. Use a reference to the function instead:
setInterval(doSome, 60000);
No, you are not guaranteed exact time safety. JS is event based (and single-threeaded) so the event won't fire at the exact right moment, especially not if you have other code running at the same time on your page.
The event will fire in the neighbourhood of the set time value, but not on the exact millisecond. The error may be tens of milliseconds even if no other event is running at the time. This may be an issue if for example you have a long-running process where the timing is important. If you do, you'll need to synchronize with a clock once in a while.
Yes it will be called as long as the page is open, regardless the tab is switched or even the browser is minimized.
However make sure you pass the function not a string to setInterval
it should be >
setInterval(doSome, 60000)
About "exact time safety": The following code starts UpdateAll at intervals of RefreshInterval milliseconds, with adjustment each second so that one start occurs at each second at the start of the second. There will be a slight delay for the finite speed of the computer, but errors will not accumulate.
function StartAtEachSecond ()
{
var OneSecond = 1000; // milliseconds
var MinInteral = 50; // milliseconds, estimated safe interval
var StartTime = OneSecond - (new Date ()).getMilliseconds (); // Time until next second starts.
if (StartTime < MinInteral) StartTime += OneSecond
window.setTimeout (StartAtEachSecond, StartTime + MinInteral); // To set up the second after the next.
for (var Delay = 0.0; Delay < OneSecond - MinInteral; Delay += RefreshInterval)
{
window.setTimeout (UpdateAll, StartTime + Delay); // Runs during the next second.
}
}