Persist setTimeout and setInterval across Node.js restarts - javascript

I set quite a few server-side timeouts with setTimeout and setInterval for each connected user that can last for 10-30 seconds. If the Node.js instance restarts in the middle of one of these timeouts, they are obviously all cleared on restart, which can cause some issues for these users. How would I go about persisting these timeouts, or are there any modules that already help with this?

setTimeOut takes delay as parameter, so when setting timeout, capture currentServerTime + delay say serverTriggerTime and persist this in DB. Then, on restart of server, create the same timer using the serverTriggerTime.
Then, delay = serverTriggerTime - currentServerTime, use this delay to set new timer.
When setting timer
const date = Date.now();
const serverTriggerTime = date + delay; // time in milliseconds
On server restart:
serverTriggerTime = // retrieve from DB.
newDelay = serverTriggerTime - Date.now();
Also, set new timer only if newDelay >= 0, meaning the trigger time has not reached and will happen after newDelay time.

I would store the start times and durations in Redis and restart incomplete timers when your application reloads. Some Redis modules:
https://github.com/joyent/node/wiki/modules#wiki-db-nosql-redis

Related

Interval synced to Time

I want to execute a function in an interval. Yeah I could use setInterval but I need the interval to be synced to the timestamp or something.
Like I want to execute the interval on two different devices and they should run in the exact same second or even ms if possible. But depending on when I star the script these intervals would be offset if I would use setInterval method.
I've already tried this but it kinda acts weird.
setInterval(() => {
if (new Date().getTime() % 1000 * 10 == 0) {
console.log(new Date().toLocaleTimeString())
}
}, 1);
Like I want to execute the interval on two different devices and they should run in the exact same second or even ms if possible.
There's no guarantee that you can do this, not least because the JavaScript thread on one of the devices may be busy doing something else at that precise moment (it could even be tied up for several seconds).
Other than that, there's the issue of synchronizing the devices. Options are:
Some kind of synchronization event you send simultaneously to both devices. You'd run your code in response to the synchronization event received from your server. This is naturally subject to network delays, it requires a server to send the event (probably over web sockets), and is subject to the above caveat about the JavaScript thread being busy.
Relying on the devices being synced to exactly the same time source (for instance, perhaps they're both using a NIST time server or similar). If you know their times are synchronized sufficiently for your purposes, you can schedule your timer to fire at a precise moment, like this:
// Fire at exactly 14:30 GMT on 2021-04-21
const target = new Date(Date.UTC(2021, 3, 21, 14, 30)); // 3 = April, starts with 0 = Jan
const delay = Date.now() - target;
if (delay < 0) {
// It's already later than that
} else {
setTimeout(() => {
// Your code here
}, delay);
}
BUT, again, if the JavaScript thread is busy at that precise moment, the timer callback will run later, when the thread is free.
The code above schedules a single event, but if you need a recurring one, you can do the same basic logic: Determine the date/time you want the next callback to occur, find out how many milliseconds it is between now and then (Date.now() - target), and schedule the callback for that many milliseconds later.

Timer with performance.now()

I want to realize timer using performance.now because Date.now depends on users clock (performance.now doesn't), and "Date.now-way" may crash the timer, when user changes system clock.
And I want to store timer in localStorage. When user close tab or browser and open it, timer have to resume.
Important condition: I want to take into account the time, when the browser/tab was closed. For example, user close browser, when timer value is 10 sec. After 10 sec user open the browser - timer value should be 20 sec.
This is the problem, that I faced.
This is code without using localStorage:
const timerNode = document.getElementById('timer');
const initialTimestamp = performance.now();
setInterval(() => {
const seconds = getSeconds(initialTimestamp);
timerNode.innerHTML = seconds;
}, 200);
function getSeconds(initialTimestamp) {
return Math.round((performance.now() - initialTimestamp) / 1000);
}
<div id='timer'></div>
Is there any way to store timer in localStorage using performance.now and resume it in cases, that I mentioned above (and observing the condition)?
Or maybe there is another way to get UNIX timestamp, that is not depends on system clock?
To demonstrate problem with Date.now() run this code and change system time, for example, set date to few days ago. You will see, that value of the timer is negative.
const timerNode = document.getElementById('timer');
const initialTimestamp = Date.now();
setInterval(() => {
const seconds = getSeconds(initialTimestamp);
timerNode.innerHTML = seconds;
}, 200);
function getSeconds(initialTimestamp) {
return Math.round((Date.now() - initialTimestamp) / 1000);
}
<div id='timer'></div>
Yes, you would use localStorage for this to persist across browser sessions.
You would use setItem to set the initial time and getItem to access it in new browser sessions. When the new browser session starts, you would check if a previous initial time existed. If it did, use it; if it didn't, create it.
var initialTimestamp = localStorage.getItem('initialTimestamp');
// If we previously set an initialTimestamp, convert it from string to number.
if (initialTimestamp) {
initialTimestamp = parseInt(initialTimestamp);
}
// If we never previously set an initial timestamp, create one now.
else {
initialTimestamp = performance.now();
localStorage.setItem('initialTimestamp', initialTimestamp);
}

What happens to setTimeout when the window is out of focus?

I have a situation where I need to reauthenticate a token on a cordova app before the authentication token expires. To do that I figured I'd set a timeout just before the auth token expires, to reauthenticate.
function authenticate() {
var token = ... get token
setTimeout(function() {
.. try to reauthenticate
}, token.expiresIn - 600*1000);
}
Problem I could see is that-
The timeout period passes while the app is sleeping. Function does not fire?
The timeout "countdown" (if that's how it works) is paused while the app is sleeping.
Neither of these are good scenarios. So my question is, what happens to a timeout while the application is out of focus? Should I instead have a 10 second interval that checks the expiration for this scenario?
Edit:
So lets say the token is for 4 hours. If the user uses the app for an hour, minimizes it for 2 hours and comes back, will the function call in an hour or 3 hours? This would be the point of the interval, so I can check the situation relatively quickly.
The timeout behavior really depends on the device type and OS version. On some, any timers that are "due" fire as soon as the application becomes active. On others (and I believe this is the case for current iOS), the timer is paused while your application is inactive and resumes when it becomes active.
For a long-running timer (i.e. your 4 hours example) you can't rely on the setTimeout() because on some devices it won't account for the inactive time. You'll need to subscribe to Cordova's resume event and re-calculate and update your timers. The following setLongTimeout() function should behave as expected in Cordoval. It's untested and would need to be expanded if you need multiple long timeouts.
var longTimeoutId, longTimeoutDate, longTimeoutCallback;
// Use instead of `setTimeout()` for a long timeout in Cordova
function setLongTimeout(callback, delay) {
if (longTimeoutId) {
clearTimeout(longTimeoutId);
}
longTimeoutCallback = callback;
longTimeoutDate = Date.now() + delay;
longTimeoutId = setTimeout(function() {
longTimeoutId = null;
callback();
}, delay);
}
document.addEventListener("deviceready", function() {
document.addEventListener("resume", function() {
if (longTimeoutId) {
setLongTimeout(callback, longTimeoutDate - Date.now();
}
});
});

Cancel Javascript timeout

I have a long process hosted on a Web Server. The thing is triggered from a Web Page, at the click of a button by a user. Some Javascript polls regularly via Ajax to check if the operation has completed server side. To do this, I use setInterval, and later on clearInterval to stop polling.
If this takes too long (e.g. server has crashed), I'd like the client to be informed by some sort of timeout. I've done some research and found about setTimeout. Problem is, if the operation finishes successfully before the timeout, I'd like to cancel this one.
How to do this ?
Would you suggest a different approach ?
PS : I'm targetting IE7/IE8 in particular, but always open to some JQuery
As long as you store your interval's id in a variable, you can use it to clear the interval at any time.
var interval = window.setInterval(yourFunction, 10000);
...elsewhere...
window.clearTimeout(interval);
For more information see the Mozilla Documentation's setInterval example.
Put together a quick JS Fiddle containing a modified version of Mozilla's Example.
To clear a setTimeout, use clearTimeout.
You want two timers (as you said)
repeating interval to do the next poll and
one-time expiration to give up if the server never responds
If the polling is successful you want to clear both the polling interval and cancel the failure timer. If the expiration timer fires you want to clear the polling interval
var checkCount = 0;
function checkComplete() {
console.log("test " + checkCount);
if (checkCount++ > 10) {
console.log("clearing timeout");
window.clearInterval(pollInterval);
window.clearTimeout(expireTimer);
}
}
function cancelPolling(timer) {
console.log("clearing poll interval");
window.clearInterval(pollInterval);
}
var pollInterval = window.setInterval(checkComplete, 500);
var expireTimer = window.setTimeout(cancelPolling, 10000);
You can fiddle with the checkCount constant "10" - keep it low to simulate polling success, raise it higher for the timeout to occur before the checkCount is reached, simulating polling failure.

How to have a timer which cannot be modified in javascript?

Basically, I am designing a quiz application with limited time. Use selects answer to a question and the next question loads using an Ajax request. All questions must be answered within a time frame of, say 2 minutes.
A clock ticks away to show how much time is left and as soon as it hits 0, results are shown. Now since the timer will be implemented using window.setTimeout(), it is possible that the value of timer variable be modified using an external bookmarklet or something like that. Anyway I can prevent this? I think this is implemented on file sharing sites like megaupload. Any forgery on the timer variable results in request for file being rejected.
Have .setTimeout() call an AJAX method on your server to synch time. Don't rely on the client time. You could also store the start time on the server for a quiz, and then check the end time when the quiz is posted.
You need to add a validation in your server side. When the client want to load the next question using an Ajax request, check whether deadline arrived.
The timer in client side js just a presention layer.
If the function runs as a immediately called function expression, then there are no global variables and nothing for a local script to subvert. Of course there's nothing to stop a user from reading your code and formulating a spoof, but anything to do with javascript is open to such attacks.
As others have said, use the server to validate requests based on the clock, do not rely on it to guarantee anything. Here's a simple count down that works from a start time so attempts to dealy execution won't work. There are no global variables to reset or modify either.
e.g.
(function (){
// Place to write count down
var el = document.getElementById('secondsLeft');
var starttime,
timeout,
limit = 20; // Timelimit in seconds
// Function to run about every second
function nextTick() {
var d = new Date();
// Set start time the first time
if (!starttime) starttime = d.getTime();
var diff = d.getTime() - starttime;
// Only run for period
if (diff < (limit * 1000)) {
el.innerHTML = limit - (diff/1000 | 0);
} else {
// Time's up
el.innerHTML = 0;
clearTimeout(timeout);
}
}
// Kick it off
timeout = window.setInterval(nextTick, 1000);
}());

Categories

Resources