I'm building a live chess app, and I'm trying to add a timer to it. However, I am struggling to find a way to make the timer accurate. I have run some tests, and setInterval and setTimeout are extremely inaccurate, with a ten minute timeout being off by over three minutes. I also tried using setInterval with in interval of 100ms, but even that was off by over minute, when the tab was not active. That was just with the javascript window.setInterval; with nodejs, it hasn't been more than 10ms off, but I'm afraid it will if the server gets busy. I'm hoping to find a way to have the game end within at least a tenth of a second of the real time.
Any suggestions? Thanks!
an ideal approach would be to use absolute clock time to get time elapsed and timer to have that check.
Something like code below.
const startTime = new Date();
const maxTime = 5 * 1000; // 60 seconds
setInterval(() => {
checkExpired();
}, 300);
function checkExpired() {
const curTime = new Date();
timeDifference = Math.abs(curTime.getTime() - startTime.getTime());
if (timeDifference > maxTime) {
console.log("time up");
}
}
The browser will not run setInterval as required due to performance reason.
https://usefulangle.com/post/280/settimeout-setinterval-on-inactive-tab
If you need to run timer with required interval then it can be run in worker thread.
some info here.
How can I make setInterval also work when a tab is inactive in Chrome?
But my guess is increased granularity is fine for non active tab.
Related
I'm curious if you can invoke a function after the time changes?
For example invoking a function after the minute changes maybe something like this:
onMinChange(()=>{console.log('the minute has changed!')})
Note:
I do not mean waiting for a minute to invoke a function like this
setInterval(()=>{console.log('the minute has changed!')},60000);
there's a difference between
✓ invoking a function after the minute changes (Current time 2:30:05, invoke function at 2:31:00 )
&
X waiting for a minute to invoke a function (Current time 2:30:05, invoke function at 2:31:05 )
There's nothing built into the web platform that will do this (nor, I think, Node.js's standard library). The closest you can come (without some third-party lib) is to get the time, calculate how long it should be until the next minute, and then use setTimeout to schecule a callback. If you want it repeated, repeat as necessary.
Here's an example:
const MINUTE_IN_MS = 60000;
function callNextMinute(fn) {
const now = Date.now();
let next = now / MINUTE_IN_MS;
const ceil = Math.ceil(next);
if (ceil > next) {
next = ceil;
}
return setTimeout(fn, (next * MINUTE_IN_MS) - now);
}
function tick() {
console.log(`New minute! Time is ${new Date()}`);
callNextMinute(tick);
}
console.log(`Started at ${new Date()}`);
callNextMinute(tick);
There, the margin of error is ~10 seconds.
Beware that:
That kind of recurring execution can have an adverse effect on battery life of mobile devices.
Timers are slowed or even stopped for tabs that aren't the active tab in many browsers, so you may well miss the minute if the tab is inactive.
You can achieve behaviour like this with the npm package cron.
var job = new CronJob('* * * * *', function() {
console.log('You will see this message every full minute');
}, null, true, 'America/Los_Angeles');
I am debugging a node.js (Node version 6.2.1) program with setInterval() in it. The code is very simple:
const log = console.log;
let cnt = 0;
const inc = () => {
const beforeDebug = Date.now();
log('Before debug time', beforeDebug);
debugger;
const afterDebug = Date.now();
log('After debug time', Date.now());
log('Time spent in debug', afterDebug - beforeDebug);
log(`[${cnt++}]`);
};
setInterval(inc, 1000);
```
The strange behaviour that I observe is that the pauses between setInterval() callback execution will depend on how much time I spend in a breakpoint. For example, if I stay in 'debugger' line for 10 seconds and then resume, I will see the next number only in 10 seconds after I resumed.
I checked this behaviour with command line node debugger and node-inspector.
[UPDATE] (added timestamps to code)
Here's an extracted lines of debug session in command line:
Debugger listening on port 5858
connecting to 127.0.0.1:5858 ... ok
< Before debug time 1467952218915
< After debug time 1467952235018
< Time spent in debug 16103
< Before debug time 1467952252123
Basically the time difference between the executions of the callback in the last section is 17105 which is almost exactly <time-in-debug> + <interval-value>
This issue doesn't have a significant practical impact, but I would love to understand the mechanism of timer underneath. Why it behaves in such weird way?
The short answer is that the time used to determine when to trigger callbacks is cached and can get out of sync with a callback's actual insertion time.
How is this possible?
To understand why this happens, it helps to have an idea of how node's timers are implemented (which is very well documented in the source). Here, all we need to remember is that:
Callbacks are stored in a map keyed by timeout. For example, when you run setTimeout(foo, 10), then foo will be added to a list of all callbacks with timeout 10.
Callbacks keep track of the time at which they were inserted. We'll refer to it as insertionTime to keep things simple (it's actually timer._idleStart in the source).
When a timeout kicks in, node records the current time (now) and runs the timeout's callbacks in sequence until either no callbacks are left or a callback's insertion time is such that now - insertionTime < timeout. In that last case, node will delay the next wakeup of this timeout until timeout - (now - insertionTime).
In your case, here is what happens:
t=0, inc is inserted in a callback list with insertionTime=0.
t=1000, the timeout wakes up, records now=1000, and runs inc.
t=3000 (or however long you are debugging for), inc completes and gets reinserted in the callback list (since you are using setInterval), this time with insertionTime=3000.
t=3000, node keeps going through the list of callbacks and finds the newly inserted callback. It computes now - insertionTime = -2000 which is smaller than timeout (because now is still equal to 1000!). It therefore schedules the next callback execution 3000 = 1000 - (-2000) milliseconds later.
t=6000, the timeout wakes up again, records now=6000, and runs inc.
...
You can get some visibility on timer internals by running your program with NODE_DEBUG=timer:
$ NODE_DEBUG=timer node setinterval.js
TIMER 98831: no 1000 list was found in insert, creating a new one
TIMER 98831: timeout callback 1000
TIMER 98831: now: 1067
inc
TIMER 98831: 1000 list wait because diff is -2000
TIMER 98831: timeout callback 1000
TIMER 98831: now: 6072
inc
TIMER 98831: 1000 list wait because diff is -2000
...
As far as I can tell, this looks like a bug in node. I don't see when it would make sense to have a negative diff above.
What can we do about it?
You seem to be asking this more out of curiosity, but in case you ever need to prevent this added delay, you can work around it by making sure that you have at least one other callback for the same timeout. This will force an update to now.
function f() {
var end = Date.now() + 2000;
while (Date.now() < end); // 2 second busy loop.
console.log('f: ' + Date.now());
}
setInterval(f, 1000);
setTimeout(function () {
setInterval(function () {}, 1000);
}, 10); // Small delay to make sure they don't run in the same wakeup.
If we only add f, it would end up running every 5 seconds. However, if we also register the (empty) second callback, f will correctly run every 3 seconds!
Node.js runs your code in one single thread.
When you pause the execution, the consequential callback will also be delayed until you resume.
Here is an alternative example without employing debugger
var blockUntil = Date.now() + 7000;
function f() {
console.log('Before: ', Date.now())
while (Date.now() < blockUntil); // busy loop
console.log(' After: ', Date.now())
}
setInterval(f, 1000);
And I can confirm it can be reproduced without debugger.
Before: 1467966150871
After: 1467966156847
Before: 1467966163851
After: 1467966163851
Before: 1467966164856
After: 1467966164856
Before: 1467966165859
After: 1467966165859
Before: 1467966166864
After: 1467966166864
My ionic app has a timer(a simple setInterval that ticks every second) which works perfectly fine when the app is in the foreground. However when the app goes to the background and comes back to the foreground after 10 minutes, the time displayed in the app is wrong (the time is much less that it should be). I have tried adding the timer into a directive and also using the native DOM manipulation api(document.getElementById, etc) methods, but they both didn't work. I think the ionic framework is doing something to the view and bindings when the app goes to the background. Has anyone experience such a issue and if so how did you guys manage to fix it?
After hours of searching for an answer, I finally came up with my own hack. I hope this solution might help others who come across a similar issue.
When the app goes to the background, at some random time, the timer stops ticking and goes to sleep till the app is brought back the foreground.
When the app comes up to the foreground, the timer starts ticking again from the point where it went to sleep.
Solution/Hack:
Record the timestamp in a separate variable(in seconds) and have it updated in each interval of the timer.
var timeStamp = Math.floor(Date.now() / 1000);
Check each interval of the timer if the difference between your previous interval's timeStamp and the latest(new) timeStamp is greater than one second. If the condition is met, add the difference between those two timestamps to your ticking time.
How it works:
App in Foreground
Just Before timer start ticking
- Time stamp recorded (Assume 1 second)
Timers start ticking
- check condition
if(currentTimeStamp - previousTimeStamp > 1)
{
Add the the above difference to the time
}
Before the interval ends, update the TimeStamp variable with the currentTimeStamp.
In the first interval, the currentTimeStamp should be either 1 second or 2 second depending on weather you are offloading the timer into a setTimeout.
Thus the difference will definitely be 0 or 1. Since the condition doesn't match we update the timestamp with 1 or 2 seconds and move on to the next interval.
As long as the timer doesn't go to sleep our condition will fail.
App in Background
Strangely after 10 minutes, the timer goes to sleep(our timer is literally losing track of time from now because the next interval is not firing).
App return from Background to foreground
The timer starts ticking from where it stopped(i.e. the next interval). Now the difference in our condition should be more than one second and thus adding that difference(basically the lost time) to our current ticking time.
Code:
var transactionTime = 0; //Initial time of timer
var timeStamp = Math.floor(Date.now() / 1000);
var deltaDelay = 1;
setInterval(function () {
if (transactionTime != 0 && (Math.floor(Date.now() / 1000) - timeStamp) > deltaDelay) {
transactionTime += (Math.floor(Date.now() / 1000) - timeStamp);
}
timeStamp = Math.floor(Date.now() / 1000);
//Update your element with the new time.
window.document.getElementById("transaction_timer").innerHTML = util.formatIntoHHMMSS(transactionTime++);
}, 1000);
Note: This solution work standalone(vanilla Js with native DOM api) and also works great in angular directives.
You can increase the deltaTime of the above code to 2 to be a little more accurate if by any chance your single thread is busy somewhere else with some other task.
P.s I'm actually running the ionic app inside my own instance of a webview and not cordova so I can't use any fancy cordova plugin.
Ionic/Cordova apps goes to sleep when in background mode. But you could look at this: https://github.com/katzer/cordova-plugin-background-mode
Here is a little bit updated code from the
original answer
var interval = null;
var timerSecondsTotal = 0;
function runTimer() {
var prevTickTimestamp = Date.now()
interval = setInterval(() => {
var currentTickTimestamp = Date.now()
var delta = currentTickTimestamp - prevTickTimestamp
timerSecondsTotal += Math.round(delta / 1000)
prevTickTimestamp = currentTickTimestamp
}, 1000)
}
I have multiple countdown timers in my app and I'm updating those values using setInterval function. The idea of following code block is the loop through all ongoing games and update countdown clocks. I have 1000ms interval but it loops much faster than that. Why? When I open multiple browser windows to see countdown timer updates I can notice that interval is something like 500ms.
if (Meteor.isClient) {
Meteor.setInterval(
function() {
var going = Games.find({isGoing: true});
var goingFetch = going.fetch();
var goingCount = going.count();
for (i = 0; i < goingCount; i++) {
Games.update(goingFetch[i]._id, {$set: {timeLeft: goingFetch[i].timeLeft - 1}});
}
}
, 1000 );
}
Is there better way to solve this problem? I may have 100 games simultaneously and every game have one to many timers. Timer type can be countdown or incremental and all timers are stoppable. Interval is always and in all cases 1000ms.
UPDATE
I did more tests and setInterval works fine but clients still have too fast countdown timer.
Are you sure your code isn't executed twice by mistake? Does goingCount produce correct results, for instance? setInterval isn't as accurate as setTimeout, but it shouldn't cause problems at 1000ms. Can we see more of your code?
In a modern web browser, suppose I do a setTimeout for 10 minutes (at 12:00), and 5 minutes later put the computer to sleep, what should happen when the system wakes up again? What happens if it wakes up before the 10 minutes are up (at 12:09) or much later (at 16:00)?
The reason I'm asking is because I'd like to have a new authentication token requested every 10 minutes, and I'm not sure if the browser will do the right thing and immediately request a new token if it wakes up after a long time.
Clarifications: I don't wan't to use cookies - I'm trying to build a web service here; and yes, the server will reject old and invalid tokens.
As far as I've tested, it just stops and resumes after the computer wakes up. When the computer awakes the setInterval/setTimeout is unaware that any time passed.
I don't think you should rely on the accuracy of setTimeout/Interval for time critical stuff. For google chrome I discovered recently that any timeout/interval (that is shorter than 1s) will be slowed down to once a second if the tab where it's activated looses focus.
Apart from that the accuracy of timeouts/intervals is dependent on other functions running etc. In short: it's not very accurate.
So using interval and timeouts, checking the time against a starttime within the function started by it would give you better accuracy. Now if you start at 12:00, the computer goes to sleep and wakes up at 16:13 or so, checking 16:13 against 12:00 you are certain you have to renew the token. An example of using time comparison can be found here
Compare current datetime against datetime when the page was loaded, like so:
//Force refresh after x minutes.
var initialTime = new Date();
var checkSessionTimeout = function () {
var minutes = Math.abs((initialTime - new Date()) / 1000 / 60);
if (minutes > 20) {
setInterval(function () { location.href = 'Audit.aspx' }, 5000)
}
};
setInterval(checkSessionTimeout, 1000);
Here is my code :
<!doctype html>
<html>
<body>
<input type="button" name="clickMe" id="colourButton" value="Start Timer" onclick="setTimeout('alert(\'Surprise!\')', 120000)"/>
</body>
<script>
</script>
</html>
I have taken three scenarios that might answer the question.
Scenario 1: At 00 Seconds click on 'Start Timer' button . At 25 seconds computer falls asleep.
At 1min 40 seconds wake up computer.
At 2mins Alert is displayed.
Scenario 2 : At 00 Seconds click on 'Start Timer' button . At 26 seconds computer falls asleep.
At 3 mins, I wakeup the computer. The Alert is displayed.
Scenario 3 : This one is truly astounding.
<input type="button" name="clickMe" id="colourButton" value="Start Timer" onclick="setTimeout('alert(\'Surprise!\')', 600000)"/>
At 00 Seconds I click on 'Start Timer' button.
At around 1min 30 seconds the computer is on hibernate mode (my pc takes a minute to initiate hibernate)
At 8 mins I turn the laptop on.
At 10 mins exactly, the alert pops up.
PS: This is my first ever comment on Stack Exchange. I prefer to execute code and view results rather than infer from theory.
The behavior is based on both the browser and the operating system. The OS handle sleep and individual apps often don't account for it.
What will most likely happen is that the OS will come back up with the same time remaining on the timer as when it was shut down. The other possibility is that it won't fire at all.
If it is really a concern, you will probably want to be better safe than sorry and store a time stamp of when the token was initialized and use setInterval to check it periodically (say twice a minute).
However, security should not be just a client side thing. Make sure that your server throws an error if an old / invalid token is used and that the Ajax behaves appropriately in response.
[edit]
I agree with the other post that it might fire immediately on the next tick. Resig's blog post is very good.
Behaviour of JavaScript timers (setTimeout) in several scenarios.
When the thread is free and the timeout fires: The timer is fired immediately after the timeout. It might have certain imprecision of about 0-5 ms (typical scenario).
When the thread is super busy (huge loop) long enough to pass the timer timeout: The timer will be executed immediately after the thread is freed.
When there is an alert: Same behaviour as 2.
When the thread is paused because our laptop went to sleep: I have seen several things. But most common is total inaccuracy and ignore of the time spent during sleeping.
Since timers in JavaScript are based on CPU ticks, and the CPU is sleeping, then the timer is completely paused and resumed as 'since nothing would have happen'.
Based on Ben's answer, I created the following util. You can tweak the sampling duration, however I use it just like this for token refreshing:
const absoluteSetInterval = (handler, timeout) => {
let baseTime = Date.now();
const callHandler = () => {
if (Date.now() - baseTime > timeout) {
baseTime = Date.now();
handler();
}
};
return window.setInterval(callHandler, 1000);
};
const absoluteClearInterval = (handle) => window.clearInterval(handle);
In John Resig's blog the timers are said to be using "wall clock". I believe that the events will fire immediately after the machine is resumed because setTimeout() doesn't guarantee an execution is a specific point in time but as soon as possible after the specified interval. However, I haven't checked it myself.