This question already has answers here:
Why does setTimeout() "break" for large millisecond delay values?
(7 answers)
Closed 8 years ago.
I found myself on this jsperfs' page, why does that happen?
JSPerf smallest timeout
Any clues?
And why is 4 faster than 0 too?
There is a comment about this, you should read the article closely.
Info
The smallest setTimeout timeout value allowed by the HTML5 specification is 4 ms. Smaller
values should clamp to 4 ms.
Therefore, the first two tests below should have about the same result.
P.S. Some browsers freak out when you use a value greater than 599147937791 for the
timeout (i.e. they use 0 or 4 ms instead), hence the last test.
Essentially, Javascript has internal handling for 0 and 599147937792 as they qualify for over/underflow values for setTimeout and they are rounded to a default minimum accepted value 4 ms. This is probably because it is unreasonable to ask for a 0 ms delay as it would probably take longer than this to even process the function and determine this is what the user wants. The error on the larger value is probably due to the fact that computers have limits how big/small of a number that you can represent.
To understand why the large and small values return after 4 for example is that the internal handling takes time as well, a very small amount, but time. Consider these two timelines:
Timeline 1
setTimeout(...,0) is called
The function checks boundary conditions (something like if (time < 4) {// use 4})
The function needs an extra step here to change the value from 0 -> 4.
Now it sets the timeout for 4 ms
Timeline 2
setTimeout(...,4) is called
The function checks boundary conditions (something like if (time < 4) {// use 4})
Everything is ok, moves along.
Now it sets the timeout for 4 ms
Step 3 in the two timelines takes longer in the first case as there is the extra step of changing the value. Both will wait for the same amount of time, but the second one will start its timing ever so slightly sooner. This is much the same with 599147937792, except the check will be for the upper bound.
The phrasing "freaks out" makes me think it might look more like
try {
// try with the given input
} catch (Exception) {
// Ahh I freaked out, just use 4 instead!!!!111!!!
}
From MDN:
Browsers including Internet Explorer, Chrome, Safari, and Firefox store the delay as a 32-bit signed Integer internally. This causes an Integer overflow when using delays larger than 2147483647, resulting in the timeout being executed immediately.
Related
I understand that setTimeout doesn't necessarily fire at the exact delay you specify, because there could be other items in queue at the instant that the timeout occurs and the engine will do those things first (further delaying the time you've
specified).
However, I'm wondering if it does take sub-millisecond inputs into consideration at all. For example, if I input 1.12345678ms, behind the scenes does it attempt to fire at that exact time, or does it parseInt the sub-millisecond value I've inputed before even truly setting the actual timeout (under the hood)?
Furthermore, let's say I'm determining the ms delay with long division and that division produces an exponent like 1.2237832530049438e-9. Do I need to parseInt that exponent before handing it to setTimeout(()=>{},ms) or will setTimeout do the right thing (as long as it is some type of number) without me ever having to worry about prepping the input?
Update: Here's a snippet of setTimeout dealing with smaller and smaller sub-millisecond delay values:
let count = 0;
function consoleLog(timeOut)
{
let now = Date.now();
timeOut = (timeOut / 1.12345);
setTimeout(()=>
{
count += 1;
if (count <= 6444)
{
console.log(`Timestamp ${now}`,`Timeout: ${timeOut}`,`Count: ${count}`);
consoleLog(timeOut);
}
});
}
consoleLog(1000);
Warning, the code above recurses 6,444 times in order to show that there comes a point where the timeOut value no longer gets smaller from dividing it further: after count 6440, the timeout sustains 2e-323 thereafter.
Modern browsers throttle setTimeout/setInterval calls to a minimum of once every 4 ms.
Also, MDN says that:
The delay argument is converted to a signed 32-bit integer. This
effectively limits delay to 2147483647 ms, since it's specified as a
signed integer in the IDL.
So, any fractions of milliseconds are not going to be effective.
The times are not JS specifications - they are specified in the DOM standards.
4 ms is specified by the HTML5 spec and is consistent across browsers
released in 2010 and onward. Prior to (Firefox 5.0 / Thunderbird 5.0 /
SeaMonkey 2.2), the minimum timeout value for nested timeouts was 10
ms.
However in Node JS, the timers used are the system-specific high precision timers. They (the system timers) can go in resolutions up to nanoseconds. It should be experimented in Node JS if the timer delays are saved as integers.
ExceptionOr<int> DOMWindow::setTimeout(JSC::JSGlobalObject& state, std::unique_ptr<ScheduledAction> action, int timeout, Vector<JSC::Strong<JSC::Unknown>>&& arguments)
{
auto* context = scriptExecutionContext();
if (!context)
return Exception { InvalidAccessError };
// FIXME: Should this check really happen here? Or should it happen when code is about to eval?
if (action->type() == ScheduledAction::Type::Code) {
if (!context->contentSecurityPolicy()->allowEval(&state))
return 0;
}
action->addArguments(WTFMove(arguments));
return DOMTimer::install(*context, WTFMove(action), Seconds::fromMilliseconds(timeout), true);
}
According to source code for setTimeout it takes int as input. Which is a 32-bit signed integer.
So, the answer is, no. It does not takes into consideration.
I see in some question What is minimum millisecond value of setTimeout? people talk about the "minimal timeout of setTimeout", but I can't really understand it.
It says the minimal timeout value in HTML5 spec is 4ms, so I think, if I run following code in browsers (say Chrome):
setTimeout(function() { console.log("333"); }, 3);
setTimeout(function() { console.log("222"); }, 2);
setTimeout(function() { console.log("111"); }, 1);
setTimeout(function() { console.log("000"); }, 0);
the output should be:
333
222
111
000
But actually it is:
111
000
222
333
Seems like they still be run according to the specified timeout even if they are less than 4 (expect the 0 and 1)
How should I understand the value 4ms?
The limit of 4 ms is specified by the HTML5 spec and is consistent across browsers released in 2010 and onward.
http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#timers
To implement a 0 ms timeout in a modern browser, you can use window.postMessage()
https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
More info
https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout
https://developer.mozilla.org/en/docs/Web/JavaScript/EventLoop#Event_loop
After reading the recommended articles (thanks to #Rayon and #rafaelcastrocouto), and also this one:
http://www.adequatelygood.com/Minimum-Timer-Intervals-in-JavaScript.html
I realise that maybe I misunderstood the meaning of "minimal" delay value.
The specified timeout in setTimeout has two meanings:
The function will run when or after specified timeout (not before that)
The order will be decided by the value of the specified timeout, the smaller the earlier (and in some platform, 0 is considered the same as 1), and first added will be earlier if they have the same timeout
We don't need to concern the "minimal" delay value (e.g. 4ms) in this layer.
Then the tasks will be executed by the Javascript runtime. The runtime will take the tasks from the event queue one by one (also check if the timeout is OK), and execute them. But for some performance issue, the runtime can't really run the next task immediately after the previous one finished, there might be a tiny delay(according to different runtime implementations), and in HTML5 spec, the delay should be >= 4ms (it was 10ms in earlier browsers)
I'm currently creating a countdown using setInterval though at the moment it runs slower than it should. According to the MDN, the delay parameter is in milliseconds however it isn't accurate.
I compared my countdown to the one on my phone and the phone runs nearly 5 times faster.
var count = setInterval( function() {
if (iMil == 0) {
if (iS == 0) {
if (iMin == 0) {
if (iH == 0) {
// DONE
} else {
iH--;
iMin = 59;
iS = 59;
iMil = 999;
}
} else {
iMin--;
iS = 59;
iMil == 999;
}
} else {
iS--;
iMil = 999;
}
} else {
iMil--;
}
hours.text(iH);
minutes.text(iMin);
seconds.text(iS);
milliseconds.text(iMil);
}, 1 );
This is the main part of my script. The variables hours, minutes, seconds and milliseconds are jQuery object elements.
What I'm getting at is, is there a reason that it runs slower than it is supposed too?
setInterval() is not guaranteed to run perfectly on time in javascript. That's partly because JS is single threaded and partly for other reasons. If you want to display a time with setInterval() then get the current time on each timer tick and display that. The setInterval() won't be your timer, but just a recurring screen update mechanism. Your time display will always be accurate if you do it that way.
In addition, no browser will guarantee a call to your interval at 1ms intervals. In fact, many browsers will never call setInterval more often than every 5ms and some even longer than that. Plus, if there are any other events happening in the browser with other code responding to those events, the setInterval() call might be delayed even longer. The HTML5 spec proposes 4ms as the shortest interval for setTimeout() and 10ms as the shortest interval for setInterval(), but allows the implementor to use longer minimum times if desired.
In fact, if you look at this draft spec for timers, step 5 of the algorithm says:
If timeout is less than 10, then increase timeout to 10.
And, step 8 says this:
Optionally, wait a further user-agent defined length of time.
And, it includes this note:
This is intended to allow user agents to pad timeouts as needed to
optimise the power usage of the device. For example, some processors
have a low-power mode where the granularity of timers is reduced; on
such platforms, user agents can slow timers down to fit this schedule
instead of requiring the processor to use the more accurate mode with
its associated higher power usage.
All timeout/interval/schedule functions are excepted to be run slower.
It is a nature of computer and very common in OS that there are many things CPU need to handle and too costly(and not possible) as a real-time system.
If you read theirs API https://developer.mozilla.org/en/docs/Web/API/window.setTimeout and https://developer.mozilla.org/en/docs/Web/API/window.setInterval , it said "AFTER a specified delay" and "fixed time delay between each call". They are not saying not "on a specified time" nor "called on fixed period"
Preface: I have a demo of the problem on my personal site (I hope this is ok. If not, I can try to set it up on jsfiddle). I'm intending this question to be a little fun, while also trying to understand the time functions take in javascript.
I'm incrementing the value of progress bars on a timeout. Ideally (if functions run instantaneously) they should fill at the same speed, but in the real world, they do not. The code is this:
function setProgress(bar, myPer) {
bar.progressbar({ value: myPer })
.children('.ui-progressbar-value')
.html(myPer.toPrecision(3) + '%')
.attr('align', 'center');
myPer++;
if(myPer == 100) { myPer = 0; }
}
function moveProgress(bar, myPer, inc, delay){
setProgress(bar, myPer);
if(myPer >= 100) { myPer = 0; }
setTimeout(function() { moveProgress(bar, myPer+inc, inc, delay); }, delay);
}
$(function() {
moveProgress($(".progressBar#bar1"), 0, 1, 500);
moveProgress($(".progressBar#bar2"), 0, 1, 500);
moveProgress($(".progressBar#bar3"), 0, .1, 50);
moveProgress($(".progressBar#bar4"), 0, .01, 5);
});
Naively, one would think should all run (fill the progress bar) at the same speed.
However, in the first two bars, (if we call "setting the progress bar" a single operation) I'm performing one operation every 500 ms for a total of 500 operations to fill the bar; in the third, I'm performing one operation every 50ms for a total of 5,000 operations to fill the bar; in the fourth, I'm performing one operation every 5ms for a total of 50,000 operations to fill the bar.
What part of my code is takes the longest, causes these speed differences, and could be altered in order to make them appear to function in the way that they do (the fourth bar gets smaller increments), but also run at the same speed?
The biggest problem with using setTimeout for things like this is that your code execution happens between timeouts and is not accounted for in the value sent to setTimeout. If your delay is 5 ms and your code takes 5 ms to execute, you're essentially doubling your time.
Another factor is that once your timeout fires, if another piece of code is already executing, it will have to wait for that to finish, delaying execution.
This is very similar to problems people have when trying to use setTimeout for a clock or stopwatch. The solution is to compare the current time with the time that the program started and calculate the time based on that. You could do something similar. Check how long it has been since you started and set the % based on that.
What causes the speed difference two things: first is the fact that you executing more code to fill the bottom bar (as you allude to in the 2nd to last paragraph). Also, every time you set a timeout, your browser queues it up... the actual delay may be longer than what you specify, depending on how much is in the queue (see MDN on window.setTimeout).
Love the question, i don't have a very precise answer but here are my 2 cents:
Javascript is a very fast language that deals very well with it's event loop and therefore eats setTimeouts and setIntervals for breakfast.
There are limits though, and they depend on a large number of factors, such as browser and computer speed, quantity of functions you have on the event loop, complexity of the code to execute and timeout values...
In this case, i think it's obvious that if you try to execute one function every 500ms, it is going to behave a lot better than executing it every 50ms, therefore a lot better than every 5ms. If you take into account that you are running them all on top of each other, you can predict that the performance will not be optimal.
You can try this exercise:
take the 500ms one, and run it alone. mark the total time it took to fill the bar (right here you will see that it's going to take a little longer than predicted).
try executing two 500ms timeouts at the same time, and see that the total time just got a bit longer.
If you add the 50ms to it, and then the 5ms one, you will see that you will lose performance everytime...
I have a complex animation sequence involving fades and transitions in JavaScript. During this sequence, which consists of four elements changing at once, a setTimeout is used on each element.
Tested in Internet Explorer 9, the animation works at realtime speed (it should take 1.6 seconds and it took exactly 1.6 seconds). ANY other browser will lag horribly, with animation times of 4 seconds (Firefox 3 and 4, Chrome, Opera) and something like 20 seconds in IE 8 and below.
How can IE9 go so fast while all other browsers are stuck in the mud?
I have tried to find ways of merging the elements into one, so as to one have one setTimeout at any given time, but unfortunately it wouldn't stand up to any interference (such as clicking a different link to start a new animation before the current one has finished).
EDIT: To elaborate in response to comments, here's the outline of the code:
link.onclick = function() {
clearTimeout(colourFadeTimeout);
colourFadeTimeout = setTimeout("colourFade(0);",25);
clearTimeout(arrowScrollTimeout);
arrowScrollTimeout = setTimeout("arrowScroll(0);",25);
clearTimeout(pageFadeOutTimeout);
pageFadeOutTimeout = setTimeout("pageFadeOut(0);",25);
clearTimeout(pageFadeInTimeout);
pageFadeInTimeout = setTimeout("pageFadeIn(0);",25);
}
Each of the four functions progress the fade by one frame, then set another timeout with the argument incremented, until the end of the animation.
You can see the page at http://adamhaskell.net/cw/index.html (Username: knockknock; Password: goaway) (it has sound and music, which can be disabled, but be warned!) - my JavaScript is very messy since I haven't really organised it properly, but it is commented a bit so hopefully you can see what the general idea is.
Several things:
Your timeout is 25ms. This translates to 40fps which is a very high framerate to try to achieve via javascript. Especially for things involving DOM manipulation that may trigger reflows. Increase it to 50 or 60. 15fps should be more than fluid enough for the kinds of animation you're doing. You're not trying to display videos here, just move things around the page.
Don't use strings as the first parameter to setTimeout(). Especially if you care about performance. That will force javascript to recompile the string each frame of animation. Use a function instead. If you need to pass an argument use an anonymous function to wrap the function you want to execute:
setTimeout(function(){
pageFadeIn(0)
},50);
this will only get compiled once when the script is loaded.
As mentioned by Ben, it is cheaper to use a single setTimeout to schedule the functions. For that matter, code clarity may improve by using setInterval instead (or it may not, depends on your coding style).
Additional answer:
Programming javascript animation is all about optimisation and compromise. It's possible to animate lots of things on the page with little slow-down but you need to know how to do it right and decide what to sacrifice. As an example of just how much can be animated at once is a demo real-time strategy game I wrote a couple of years ago.
Among the things I did to optimize the game are:
The walking soldiers are made up of only two frames of animation and I simply toggle between the two images. But the effect is very convincing nonetheless. You don't need perfect animation, just one that looks convincing.
I use a single setInterval for everything. It's cheaper CPU-wise and easier to manage. Just decide on a base frame rate and then schedule for different animation to start at different times.
Well, that's a lot of javascript (despite the "quadruple-dose of awesomeness" :)
You're firing a lot of setTimeout sequence, I'm not sure how well JS engines are optimised for this.. particularly IE <= 8
Ok, maybe just a rough suggestion... You could maybe write a small timing engine.
Maintain a global object that stores your current running timed events with the function to run, and the delay...
Then have a single setTimeout handler that check against that global object, and decreases the delay at each iteration and call the function when the delay becomes < 0
you global event would looks something like that:
var events = {
fade1 : {
fn : func_name,
delay : 25,
params : {}
}
fadeArrow : {
fn : func_name,
delay : 500,
params : {}
}
slideArrow : {
fn : func_name,
delay : 500,
params : {
arrow:some_value
}
}
}
then create a function to loop through these at an interval (maybe 10 or 20 ms) and decrease your delays until they complete and fire the function with params as a paramer to the function (check Function.call for that).
Once down, fire setTimeout again with the same delay..
To cancel an event just unset the property from the events list..
Build a few method to add / remove queued events, update params and so on..
That would reduce everything to just one timeout handler..
(just an idea)