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

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();
}
});
});

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.

Building an alarm clock style application

I am building a webapp (referred to as the "noticeboard") for a friend's business, to aid with their packaging and dispatch operation. It is built using HTML, CSS & JS. The backend is built in PHP / MYSQL.
The noticeboard is for the benefit of their staff and displays dispatch cut-off ("event") times, i.e as follows:
Dispatch Time 1 : 09:00
Dispatch Time 2 : 11:30
Dispatch Time 3 : 14:30
Dispatch Time 4 : 16:00
They update these times on a regular basis, as their schedule depends on their delivery firm's schedule. There is an AJAX request running every 15 mins which simply fetches the latest times (JSON format) from the database and updates the noticeboard. Although I could just simply implement an "auto browser refresh" every 15 minutes, I found this was a bit inconsistent and sometimes a "page cannot be found" error message would be displayed.
The noticeboard also displays a real-time clock. I have built this using moment.js.
The system runs 24/7 in a Chrome browser running on Windows 10. Nothing else is running on the machine.
At the moment the noticeboard simply displays these times. I need to take this one step further and make it function almost like an alarm clock. What I'm basically looking to achieve is 15 minutes before each event, it needs to highlight the upcoming event time (i.e. using jQuery addClass()). Then as soon as that event time is reached, play a buzzer sound (some kind of MP3 file). This needs to happen automatically every day for every event. Remember the event times are always changing, so it would need to be smart enough to recognise this.
What techniques can I use to achieve this functionality? I have been reading up on things like setTimeout() and setInterval(), however I'm not sure these are able to "auto-update" themselves once they have been set (i.e. if an event time changes). Do I need to look at a nodeJs based solution? I don't have any experience in nodeJs but if that's the best way to achieve this then I'm willing to give it a go. Otherwise I'm more than happy to try out something in vanilla JS.
Here's how I would approach it using setTimeout() but obviously this doesn't dynamically update:
// set the number of ms to 15 mins before the event time
var eventInterval = 36000000;
// setTimeout function for the event
setTimeout(function() {
// add "active" class to highlight the event
$('.event').addClass('active');
// after 15 mins have elapsed, remove the "active" class
setTimeout(function() {
$('.event').removeClass('active');
}, 90000);
}, eventInterval;
Your approach is fine, however, you need to do that EVERY TIME you get an AJAX response. setTimeout returns a timeoutId, which then you can use for cancelling the timeout with clearTimeout(timeoutId).
var reminderTime = 15 * 60 * 1000;
var timeoutIds = [];
function setTime(timestamp) {
// Set the interval 15 minutes before the event time.
var interval = timestamp - reminderTime;
var timeoutId = setTimeout(function() {
// add "active" class to highlight the event
$('.event').addClass('active');
// after 15 mins have elapsed, remove the "active" class
setTimeout(function() {
$('.event').removeClass('active');
}, 90000);
}, interval);
timeoutIds.push(timeoutId);
}
$.get("http://myserver/getTimes", function(times) {
// Reset all the setTimeouts
timeoutIds.forEach(function(timeoutId) {
clearTimeout(timeoutId);
});
// Assuming times is an array of timestamps
times.forEach(setTime);
});

long-poll jQuery.ajax() fails to callback after phone sleeps?

My web app uses the 'long poll' method to keep up to date with the latest data from my server. The server only responds when it has new data, which can be many minutes apart. (It is a heating control system where you only see updates when room temperatures changes or somebody changes the settings).
var version = "0";
function updater() {
$.ajax({
type: "POST",
url: "/listen",
data: version,
success: function (data) {
version = handleUpdates(data);
updater();
},
error: function () {
setTimeout(updater, 1000);
}
});
}
It works fine on desktop browsers and on phones except in one case. I have found that on android phones with Chrome something odd happens after the phone has gone to sleep for more then about 10 minutes. The post request seems to be dropped, which I guess is reasonable since the phone is asleep. In the Chrome debugger's Network tab, the Status Text of the POST request says (canceled).
The problem is when I wake the phone up while the request is cancelled, neither the success() or error() function is called, and my web app never gets updated. $.ajax() has broken its promise to call me back.
The problem only happens on some devices. I have been able to do a few ad-hoc tests by borrowing devices off friends. So far I have only seen the problem on android phones. But not is the phone is connected to a charger. I have not seen it on any tablets, on apple devices or windows PCs.
I have tried adding a timeout to the ajax settings:
timeout: 120 * 1000,
This helps because the error() function is eventually called up to 2 minutes after the wake up. But I'd like the user to see updates within 1 or 2 seconds. I don't want to make the timeout so short because it would create unnecessary server traffic.
I have also tried detecting whether device is asleep by looking for lateness in a one second setInterval as described in Can any desktop browsers detect when the computer resumes from sleep?.
When I detect the wake up, I abort() the post and start another. This helps in most cases. But it turns out to be unreliable. Sometimes time events seem to keep ticking normally during sleep and the post request gets cancelled anyway. And it it does not feel like a reliable fix.
I am using latest version of jQuery: (2.1.2) and Chrome (47).
I not sure this will work or not, I cannot test it now but give it a try
$(window).focus(function() {
updater();
});
I've had problems in the past with JavaScript calls getting suspended when the phone goes to sleep. The solution I ended up with was to use window.setInterval() which seems to suspend, but come back to life when the phone is woken up.
So I would recommend setting an interval which cancels the call every so often and reinitiates it. This might help it survive through a phone sleep.
Something roughly like:
var myCall = $.ajax({...});
Window.setInterval (refreshCall(), 10000);
function refreshCall (){
myCall.abort ();
myCall = $.ajax({...});
}
How about a higher-level watcher function like this:
var restartTimer = null;
function updater(){
$.ajax({
type: "POST",
url: "/listen",
data: version,
success: function (data) {
version = handleUpdates(data);
clearTimeout(restartTimer);
updater();
},
error: function () {
clearTimeout(restartTimer);
setTimeout(updater, 1000);
}
});
}
// Kick it when the phone wakes up.
$(window).focus(function(){
restartTimer = setTimeout(function(){
initializeAll();
}, 6000);
updater();
});
You know that the $(window).focus will fire when the phone wakes up, so you try updater() as Almis suggests, but with a fail-safe timer. If updater fires (on laptops or iOS), the timer is canceled and all is well, but if updater is dead, the fail-safe timer fires in 6 seconds and reboots your entire app by calling initializeAll().
How about a setInterval, that stores the time it was called, then compares the last time it was called to the current time - if your interval is 10 seconds and the time passed since the last run was 5 minutes, you can assume you've just woken from sleep? Then abort the current ajax call, and restart it.
The best answer is "don't do that". You're having the server wait to respond while it tracks for changes at the server side. Just have the server respond and have the jQuery ping on an interval. You can include lastchanged or haschanged if you want to prevent actually refreshing when there's no status change, but if the server is doing the same work either way, just let it respond and wait for the next poll.
setInterval(function () {
$.post({"/listen", version, function (data) {
if(data.haschanged)
version = handleUpdates(data);
}).fail(function () {
// any error correction on failed call
});
}, 1000);

Loop function for specified time, followed by delay

I have a small piece of code that continuously clicks a button called "See Older Messages" every 500 ms, in order to load infinitely-scrolled content from a webpage. Reasons for doing this are personal, but needless to say, I'm trying to automate something which would take me weeks of non-stop scrolling to do otherwise.
The problem is that the 500 ms delay gradually begins to drop as the script runs over time. After so many hours, it can take 5 seconds or more. I'm assuming this problem is caused by Facebook throttling my requests after so long, so to prevent this, I want to make the script run for an amount of time - say 2 minutes - followed by a delay of maybe 20 secs before it runs again for 2 mins, and so on. How would I go about doing this? I've racked my brains, but my limited knowledge of JavaScript hasn't come up with anything meaningful.
Below is the current code in its entirety.
setInterval(function () {
document.getElementById('see_older').getElementsByClassName('content')[0].click();
}, 500);
Thanks a lot in advance.
Keep track of when the script running started
While it's been less than 2 mins, keep clicking every 500ms.
After running for ~2 mins, stop and queue next run in 20s.
Go to step 2.
-
var lastChange;
function doClick() {
if (new Date() - lastChange < 120000 /* 2 mins */) {
document.getElementById('see_older').getElementsByClassName('content')[0].click();
setTimeout(doClick, 500);
} else setTimeout(runScript, 20000 /* 20s */);
}
(function runScript() {
lastChange = new Date();
doClick();
})();
-
I recommend using setTimeout over setInterval since, if the browser takes a while to execute, loses focus and stops executing JS, gets paged out, etc., then you will still get the time spacing between events that you want. See https://stackoverflow.com/a/731625/1059070.
Toggle whether or not your function does anything by setting another timer.
/* When true do load else don't. */
window.doLoad = true
setInterval(function () {
if window.doLoad {
document.getElementById('see_older').getElementsByClassName('content')[0].click();
}
}, 500);
/* This will toggle doLoad every two minutes. */
setInterval(function () {
if (window.onLoad == true) {
window.doLoad = false;
} else { window.doLoad = true; }
}, 120000); // two minutes of milliseconds
In your case though you might be better off using the Facebook Graph API.
Graph API documentation from Facebook
Here's an existing question with the API using Python to do basically the same thing you want to do.
JS question, also similar

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.

Categories

Resources