I have a setInterval function that do countdown of time.
startCountDownWorker() {
this.worker= setInterval(() => {
this.countDown--;
// When count timer reach zero
if (this.countDown === 0) {
// Clear decrement for count down
clearInterval(this.worker);
}
}
}, 1000);
}
The function was tested on Chrome,Firefox,Opera,Safari, its working fine on all except Firefox. When lost focus on Tab/Window on Firefox, the function stopped and only resume when I focus back on the Tab. Any ways to solve this issue ??
Browsers do apply a threshold to almost all timing functions when the page is blurred, so all these timed-out functions may only execute once in a while.
Not only this, but there is even an incoming Page Lifecycle API that is being drafted and which would add a discarded and a frozen state to the page's visibility, during which your script would not execute at all, until unfrozen.
So the best in your position, to be future proof and since you apparently only need a quite long interval, might be to handle the visibility changes and to record the time at which the page got blurred, then when it's focused back, to update your counter based on the actual elapsed time.
An other even more reliable solution would be to not actually hold an actual countDown value in your counter, but rather to always compute the delta between the starting time of that counter and now. This way, you don't rely on the precision of the browser's timers and you can adjust your timeouts between each tick.
Related
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.
I am developing a stopwatch application using Javascript/jQuery. The problem is that the milliseconds value is out of sync with REAL milliseconds. I am using function setInterval() with the interval of 1 millisecond, still it is causing this problem.
jsFiddle: http://jsfiddle.net/FLv3s/
Please help!
Use setInterval to trigger updates, but use the system time (via new Date()) for the actual time calculations.
To be honest, I tried nearly the same thing as you do now (Creating an accurate Metronome in Javascript only) - to make a long story short: To be absolutely accurate in terms of milliseconds (or lower) is sadly not (yet) possible with javascript only.
For more insight i recommend this question: Does JavaScript provide a high resolution timer?
or to be more precise this blog article: http://ejohn.org/blog/how-javascript-timers-work/
Best regards,
Dominik
Program execution in any language, not just JavaScript, is not realtime. It will take a tiny amount of time to actually run the code to increment your counter and update the view, and that throws the "timing" off.
Additionally, many browsers have a "minimum timeout" length, which varies between 4 and about 16 (the latter being the computer's own clock timer), which will really mess with your code.
Instead, you should use delta timing.
var startTime = new Date().getTime();
setInterval(function() {
var elapsed = new Date().getTime()-startTime;
// update view according to elapsed time
},25);
If you're worried about it looking choppy, consider using requestAnimationFrame instead, to update the timer exactly once per frame - this has the added benefit of not updating when the user switches tabs (but it will still be timing them) because if the tab is not active then there's no need to redraw stuff.
You can use the new performance object to get a more accurate time. Combine this with requestAnimationFrame instead of setInterval:
var startTime = performance.now(),
currentTime,
isRunning = true;
loop();
function loop(timeElapsed) {
currentTime = performance.now();
if (isRunning) requestAnimationFrame(loop);
}
Just subtract startTime from currentTime.
I left timeElapsed which contains time elapsed handed by rAF which you can may use also for something (or just ignore it).
One note though: not all browsers support this yet (and some may use prefix) so for mobile you need to use the standard system time object.
I have a function that runs on a click event that uses javascript's setIterval for some of my animations (i'm doing a game) so the problem is that if a user clicks while the animation is still displaying (setInterval is still executing) the setInterval is stacking up in the event stack or that is what I found out thus either crushing my game or running twice as fast (the animation). My question is is there any way to prevent event stacking? I do not want the setInterval to stack up on the previous setInterval and so on. I know that I could use clearInterval function like so:
var timerInterval = setInterval(drawPlayerRunning, 50);
clearInterval(timerInterval);
but it does not really work as I want it to, because what if user clicks many times while the function is still is executing, the clearInterval will only get rid of last event of the event stack leaving all the previous ones still in the "game". Any idea how to prevent this event stack up, or at least removing them efficiently?
You can create a flag that monitors the interval state:
1)
var isIntervalInProgress = false;
setInterval(function()
{
if ( isIntervalInProgress )
return false;
isIntervalInProgress = true;
drawPlayerRunning();
isIntervalInProgress = false;
}, 50);
or just a timeout that will run itself once it's finished:
2)
var func = function()
{
setTimeout(function()
{
drawPlayerRunning();
func();
}, 50)
}
whichever you like
You want to use requestAnimationFrame. It is designed with games in mind, and if your code happens to be too slow, it will reduce your frame rate accordingly (from 60 fps to 30 fps for instance). But it won't stack-up events.
Edit: Sorry, I think I misunderstood your question. Let me try again.
You should have only one draw function which is called every few milliseconds (set the interval up with requestAnimationFrame(draw)).
A click should not add a new interval, but rather create a floatingAnimation object and add it to the list of objects to render. All animation objects will be rendered by the draw function everytime the browser calls draw. In the arguments passed to draw, there will be a timestamp. Use this timestamp minus the creation date of floatingAnimation to determine how to draw the floating thing above the character.
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.
}
}
I have wrote this code to make seconds (with decisec & centisec) counting up.
You've wasted time <span id="alltime">0.00</span> seconds.
<script type="text/javascript">
function zeroPad(num,count)
{
var numZeropad = num + '';
while(numZeropad.length < count) { numZeropad = "0" + numZeropad; }
return numZeropad; }
function counttwo() {
tall = document.getElementById('alltime').innerHTML;
if(parseFloat(tall) < 1.00) { tnew2 = tall.replace('0.0','').replace('0.',''); }
else { tnew2 = tall.replace('.',''); }
tnum = parseInt(tnew2) + 1;
//check if have to add zero
if(tnum >= 100) { tstr1 = tnum + ''; }
else { tstr1 = zeroPad(tnum,3); }
tlast = tstr1.substr(0,tstr1.length - 2) + '.' + tstr1.substr(tstr1.length - 2);
document.getElementById("alltime").innerHTML = tlast;
}
var inttwo=setInterval("counttwo()",10);
</script>
In HTML document and run.
It works well but when I use Firefox 4 and run the code. Seems like it LAG a bit (stop a bit before counting up) when it's on some numbers (randomly like 12.20, 4.43). I've tried change "counttwo()" to counttwo but that doesn't help.
I have told some of my friends to run on Firefox 4 too. They said it doesn't lag at all. This cause because of my computer ? or My Firefox ? or something else ?
Thanks in advance!
PS. Fiddle here: http://jsfiddle.net/XvkGy/5/ Mirror: http://bit.ly/hjVtXS
When you use setInterval or setTimeout the time interval is not exact for several reasons. It is dependent on other javascript running, the browser, the processor etc. You have to take a reliability of +- 15ms for granted afaik. See also ...
That's a lot of counting, so on some computer, yes, it might lag (if it's a prehistoric one or the user's got his processor really busy with something), also if I'm right, that thing won't work with Chrome's V8, since that script would freeze if you switched tabs, and resume executing only when you return to that tab.
If you're just seeing pauses every so often, you're probably seeing garbage collection or cycle collection pauses.
You can test this by toggling your javascript.options.mem.log preference to true in about:config and then watching the error console's "Messages" tab as your script runs. If the GC/CC messages are correlated with your pauses, then they're the explanation for what you see.
As for why you see it but others don't... do you see the problem if you disable all your extensions?
The problem with setInterval is that it can eventually lead to a back-up. This happens because the JavaScript engine tries to execute the function on the interval (in your case, 10ms), but if ever that execution takes longer than 10ms, the JS engine starts trying to execute the next interval before the current one stops (which really just means it queues it up to run as soon as the previous callback finishes).
Since JavaScript executes single-threaded (with the exception of web workers in HTML 5), this can lead to pauses in your UI or DOM updates because it is continuously processing JavaScript callbacks from your setInterval. In worst case scenarios, the whole page can become permanently unresponsive because your stack of uncompleted setInterval executions gets longer and longer, never fully finishing.
With a few exceptions, it is generally considered a safer bet to use setTimeout (and invoking the setTimeout again after execution of the callback) instead of setInterval. With setTimeout, you can ensure that one and only one timeout is ever queued up. And since the timers are only approximate anyway (just because you specify 10ms doesn't mean it will happen at exactly 10ms), you don't typically gain anything from using setInterval over setTimeout.
An example using setTimeout:
var count = function(){
// do something
// queue up execution once again
setTimeout(count, 10);
};
count();
One reason why you may see pauses on some browsers, and not others, is because not all JavaScript engines are created equal :). Some are faster than others, and as such, less likely to end up with a setInterval backup.
Different browsers use different JavaScript engines, so it's possible that this code just finds a spot where Firefox's JägerMonkey scripting engine has some problems. There doesn't seem to be any obvious inefficiencies in the counting itself..
If it's working on your friends' installs of FF4, then it's probably just an isolated problem for you, and there isn't much you'll be able to do by changing the code.