setTimeout runs function immediately - javascript

I am trying to use 'setTimeout' to run 'window.open' function after certain period of time. If I calculate the time difference with the parent's property and then use the output for 'setTimeout', the 'window.open' function runs immediately. However, if I just give a number to the variable 'diffms', it works fine.
I am using react and how can I fix this problem?
// const diffms = 10000;
const diffms = moment(this.props.schedule.time).diff(moment());
setTimeout(() => {
window.open("https://www.google.com");
}, diffms);

SetTimeout has Maximum Delay Value
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 2,147,483,647 ms (about 24.8 days), resulting in the timeout being executed immediately.
Please check more details here:
https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#:~:text=Maximum%20delay%20value&text=This%20causes%20an%20integer%20overflow,the%20timeout%20being%20executed%20immediately.

Related

Can setTimeout's delay argument be effected at all by sub-millisecond inputs?

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.

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

setInterval 0 does not work in IE8

I have a method changeColor that updates the CSS on some elements in my HTML.
I also have a timer that controls this being applied i.e:
var timer = setInterval(changeColor,0);
The problem i'm facing is using that time interval of 0 results in the changeColor method not being run, however if i change it something minimal like:
var timer = setInterval(changeCalendarColor,1);
it works.
Now i'd be happy to use this however in IE8 this causes a slight delay in the colors appearing.
Any ideas on how to resolve this?
Thanks.
The setInterval function takes a function to call and a delay in milliseconds. You cannot have a delay of 0 milliseconds; there is a minimum delay in place (which according to the specs is 4ms). Take a look at the documentation for more.
// To call a function every second:
var timer = setInterval(myFunction, 1000);
// This doesn't throw an error as the 0 is being overridden by a default minimum:
var timer = setInterval(myFunction, 0);
If you want to call the function initially and ALSO every second after that, you should call the function when you set the interval:
var timer = setInterval(myFunction, 1000);
myFunction();
Here's what the Mozilla docs say about the minimum delay:
"In fact, 4ms 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."
Regarding the slowness on IE8, the setInterval "lag" is probably being caused by IE8 being too slow to keep up with what the function is trying to do. At each time interval, the function is called, but IE8 is queue is being overloaded as a result -- to the point that IE8 can't keep up. Increasing the delay would mask this issue I'd imagine.
As Vasile says on this Google Code forum:
"When a new call is triggered if the previous call is not ended then the new call is queued and will wait for it's time to be executed; and here is the problem... (the timer will slow down when the queue will grow and the performance will go down over time)"
Note that this is a common problem for low delays in IE8; check this post for more on this specific issue.
Also, a quick point to note about setInterval delays is that inactive tabs are occasionally treated differently:
"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 this related SO post for more.

Safari and requestAnimationFrame gets DOMHighResTimestamp; window.performance not available

I created an animation using requestAnimationFrame. Works fine in Windows Chrome and IE; Safari (tested Safari 6 and 7) breaks. It turns out that rAF get a DOMHighResTimestamp rather than a Date timestamp. That's all fine and good, and what I expected, as it's now part of the spec. However, as far as I've been able to find, there's no way to get the current DOMHighResTimestamp (i.e. window.performance is not available, even with a prefix). So if I create the start time as a Date timestamp, it behaves radically wrong when I try to determine progress within the rAF callback (very small negative numbers).
If you look at this JSBin on Safari, it won't animate at all.
In this JBin, I've made a change to "skip" the first frame (where the time parameter is undefined), so that startTime gets set to the time parameter on the next frame. Seems to work, but skipping a frame seems a bit crappy.
Is there some way to get the current DOMHighResTimestamp in Safari, given the lack of window.performance? Or alternately, force rAF into some sort of legacy mode that forces it to get a Date timestamp instead?
Does anyone know why Safari has this inconsistency, where it provides the parameter in a format that you can't get at any other way?
Performance.now() is only a recommendation as of now. https://developer.mozilla.org/en-US/docs/Web/API/Performance.now() I can only assume it's a matter of time before it's official, seeing as how everyone but Safari has it built in.
Besides that fact use this to your advantage. Since you know requestAnimationFrame returns a DOMHighResTimestamp use that for your timing.
Game.start = function(timestamp){
if(timestamp){
this.time = timestamp;
requestAnimationFrame(Game.loop);
}else{
requestAnimationFrame(Game.start);
}
}
Game.loop = function(timestamp){
Game.tick(timestamp);
... your code
requestAnimationFrame(Game.loop);
}
Game.tick = function(timestamp) {
this.delta = timestamp - this.time;
this.time = timestamp;
};
What I do here, is call Game.start which will begin the loop (I've run into situations where the timestamp was undefined so we try until we get something valid). Once we get that we have our base time and since RAF is going to return a timestamp our tick function never has to call Date.now or performance.now as long as we pass it the timestamp returned by requestAnimationFrame.

requestAnimationFrame is passing unexpected parameters in IE10

So I've been a good net citizen, using feature detection to see whether the browser supports requestAnimationFrame and only fall back to a setTimeout-based solution otherwise (something around the lines of Paul Irish's famous post).
var NOW = Date.now || function () { return new Date.getTime(); };
var reqAnimFrame =
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
/* ... || */
function (callback) {
setTimeout(function () { callback(NOW()); }, 1000 / 60);
};
var previousTime = NOW();
function animStep(time) {
var timePassed = time - previousTime;
myCharacter.move(myCharacter.speed * timePassed);
previousTime = time;
reqAnimationFrame(animStep);
}
// start the animation
reqAnimationFrame(animStep);
This worked great everywhere until Internet Explorer 10 came along. In IE10, the time parameter passed doesn't seem to have anything to do with the current time, screwing up the calculation of timePassed.
What's going on?
All (as far as I know) other browsers that implement requestAnimationFrame go by the specification in the (at the time of writing) current Working Draft:
Let time be [the redraw time] expressed as the number of milliseconds since 1970-01-01T00:00:00Z.
That's representing time precisely like your NOW() does.
IE10 however goes by the spec in the current Editor's Draft:
Let time be the result of invoking the now method of the Performance interface within this context.
Which essentially means the number of milliseconds since the browser loaded this page (it also means the the measurement is more precise, since performance.now returns fractional milliseconds).
And thus when you calculate timePassed for the first time in IE10, you are getting something like negative 43 years.
Fortunately, because the value passed to the requestAnimationFrame callback has the same unit in either case, just a different point of reference, you can easily adjust to this.
There are three possibilities:
You could just throw away the very first animation step, using it only to set previousTime, but not doing anything else.
You could ignore the passed parameter and use your NOW() (or performance.now) every time, thus always having the same same point of reference.
Or you could change the start of the animation to this:
// start the animation
reqAnimationFrame(function (t) {
previousTime = t - (NOW() - previousTime);
animStep(t);
);
This will make the calculation (including the first one) of timePassed correct no matter which spec the browser follows. And since it changes only the very first invocation, you don't have any additional overhead in the long run either.

Categories

Resources