Cordova/PhoneGap: setInterval() / setTimeout() not working correctly - javascript

I implemented a simple countdown timer using window.setInterval. It works perfectly in my desktop browser but it does not work correctly on my smartphone (Fairphone 2) as a PhoneGap/Cordova app. According to my examinations and my research on the internet the interval/timeout is interrupted when the phone is sent to sleep/standby. That's why it does not work.
Astonishingly the interval/timeout is not interrupted when my phone is connected via usb cable to my computer. So probably it's an energy-saving feature that's causing the bad behaviour.
So, I'm lost. I don't know how to implement my simple countdown timer which of course should also work when the phone sleeps (=display turned off). Is there an alternative for window.setInterval() / window.setTimeout() ?
Here is my simple code (as stated: window.setTimeout does not work, either):
...
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="js/libs/jquery.js"></script>
<script>
var min = 25;
$(document).ready(function(){
intervalID = window.setInterval(function () {
--min;
if (min > 0) {
$("#countdown").text(min);
}
}, 6000);
});
</script>
...
<p id="countdown">0m</p>

Use the interval timer only for updating the display. Use the system time to decide what to display.
Then, if your interval doesn't get called when the display is not visible, this is no problem. The next time it does get called (when the display is turned on again), it will calculate the correct elapsed time from the system time and it will display correctly.
For this reason (and several others), you should never assume that setInterval() is going to keep perfect time. In Javascript, it just means you want to be called every once in a while and you get to set an approximate time to specify the frequency, but the interval may be shut off for long periods of time.
Get the time when your interval starts with Date.now() and then each time the interval fires, get the new system time and subtract from the start time to see how much time has elapsed and then calculate what you want to display.
If you want to show minutes remaining on a 25 minute timer, you could do this:
function showTimer(selector, minutes) {
var startTime = Date.now();
var interval;
function showRemaining() {
var delta = Date.now() - startTime; // milliseconds
var deltaMinutes = delta / (1000 * 60);
if (deltaMinutes < minutes) {
// display minutes remaining
$(selector).text(Math.round(minutes - deltaMinutes));
} else {
$(selector).text(0);
clearInterval(interval);
}
}
interval = setInterval(showRemaining, 15 * 1000);
showRemaining();
}
$(document).ready(function(){
showTimer("#countdown", 25);
});
Working demo: https://jsfiddle.net/jfriend00/807z860p/

I found a cordova plugin which executes timeouts/intervals even when the display is turned off:
cordova-plugin-timers (on github)
The answer of jfriend00 was also helpful.

Related

Basic javascript countdown timer that is accurate even on mobile while screen is off [duplicate]

This question already has answers here:
How to create an accurate timer in javascript?
(15 answers)
Closed last month.
Basically, I am trying to emulate something just like https://www.online-stopwatch.com/countdown-timer/ for use in a web app. Works fine using settimeout, but when the screen is turned off on Android, for example, javascript stops running (or is severely delayed) so a 5 minute timer might take 15 minutes to ring, or 5 minutes to ring.
Any solution would be much appreciated. I've also tried support workers but those also get stopped when the screen is off.
I can't figure out what technology they are using on the aforementioned website, but it looks like animation or something, and from my tests IS accurate even when the screen is off.
Thank you!
Edit: Nope - I did a number of more tests and even their timer does not work reliably on android.
Edit: the question was actually about a website being able to trigger some kind of sound, alert or notification while the phone screen is off, not about keeping track of the time. That is not possible due to permissions (thankfully).
Apps have access to additional permissions that might make this feasible to do as an app.
Original answer basically answering "How to measure elapsed time in a browser on mobile from clicking a button even when screen is off or browser is closed":
What I would do is as soon as the timer is started you store the timer start time in localstorage and then use a setInterval to update the UI based on your stored value. I would make this accurate to the second to avoid a very heavy UI update cycle. Something like this works (tested on Android / Opera)
(Cannot use a snippet due to localStorage allow-same-origin)
<div>-</div>
<button>Start</button>
<script>
const out = document.querySelector("div")
const btn = document.querySelector("button")
let timer, startTime;
btn.addEventListener("click", () => {
// If timer is started, reset
if (timer) {
clearInterval(timer)
}
startTime = Date.now()
localStorage.setItem("startTime", startTime)
timer = setInterval(() => {
const now = Date.now()
const millis = now - Number(localStorage.startTime)
const seconds = Math.floor((millis / 1000) % 60)
const minutes = Math.floor((millis / 1000 / 60) % 60)
out.innerText = minutes + ":" + seconds
}, 1000)
console.log("Started interval timer", timer)
})
</script>
You could make it even work between browser shutdowns by checking the localstorage during page load

Keep the JS/jQuery code working in Safari when the tab is not active

I have a JS/jQuery code as shown below in which in which I want to keep the JS/jQuery code working when the session tab is not active.
The following code perfectly fine in Google Chrome but it doesn't work in Safari.
jQuery(document).ready(function ($) {
let lastActivity = <?php echo time(); ?>; // Line A
let now = <?php echo time(); ?>;
let logoutAfter = 3600; // page will logout after 1800 seconds if there is no activity
let userName = "<?php echo $_SESSION['user_name']; ?>";
let timer = setInterval(function () {
now++;
let delta = now - lastActivity;
console.log(delta); // Line A
if (delta > logoutAfter) {
clearInterval(timer);
//DO AJAX REQUEST TO close.php
$.ajax({
url: "/control/admin.php",
type: 'GET', // GET also fine
data: {action: 'logout', user_name: userName},
success: function (data) {
window.location.href = "admin.php";
},
error: function (jqXHR, textStatus, errorThrown) {
alert(textStatus);
}
});
}
}, 1000); //<-- you can increase it( till <= logoutAfter ) for better performance as suggested by #"Space Coding"
});
The value at Line A doesn't get incremented in Safari when the tab is not active but it works perfectly fine in Google Chrome. In Google Chrome, it works as expected.
You can replace counter (it counts seconds) with calculating time difference.
let lastActivity = new Date();
let logoutAfter = 3600;
...
let delta = (new Date()).getTime() - lastActivity.getTime();
if (delta > logoutAfter) {
...
}
P.S. So it must work even if the script itself is frozen when tab is inactive. Interval handler will be called at the moment when user activate this tab.
This approach will not work properly with multiple tabs opened. If user open new tab and started working in it, the earlier tab will logout the user as he is not active in that tab.
To overcome this, I will suggest to check the last active time from server using ajax call instead of doing it with javascript only.
According to this very thorough (but old) answer, setInterval() execution on inactive tabs is limited to max 1/s, on both Safari and Chrome - but not stopped. There are also plenty of questions here on SO about Javascript getting paused or de-prioritised on inactive tabs, some of which include solutions:
How can I make setInterval also work when a tab is inactive in Chrome?
iOS 5 pauses JavaScript when tab is not active
Safari JavaScript setTimeout stops when minimized
Chrome: timeouts/interval suspended in background tabs?
Probably the best option to do what you are trying is to use Web workers:
Web Workers are a simple means for web content to run scripts in background threads. The worker thread can perform tasks without interfering with the user interface.
There is an example of how to do that in an answer to one of the questions above.
But there is also a much simpler option, though you should evaluate if it is safe considering you are relying on this to log users out.
My testing of your code reflects the question I linked to earlier which describes setInterval() being slowed, but not stopped. For me, Safari (v 13.1, macOS 10.14.6) does not actually fully pause Javascript, but slows down execution of the loop, by increasing amounts. I see this by opening the dev console, and watching the output of the console.log(delta) messages - they slow right down, first running only every 2s, then 4s, and so on, though sometimes faster. But they do not stop.
That output also gives a hint about the problem, and the solution. The delta values shown on the console do not represent the real time difference since lastActivity. They are just incrementing numbers. If you see a delta value appear on the console 10 seconds after the last one, it should logically be +10, right? But it is not, it is just one higher.
And that's the problem here - the code is not counting the true time difference, it is just counting iterations of the loop:
let timer = setInterval(function () {
now++; // <-- problem
This code correctly sets now to the current time only if setInterval() runs exactly every second. But we know that when the tab is inactive, it does not. In that case it is just counting the number of times the loop runs, which has no relation to the real time elapsed.
To solve this problem, we have to determine now based on the real time. To do that, let's switch to using JS to calculate our timestamps (PHP is rendered only once, on page load, so if you use it inside the loop it will just stay fixed at the initial value):
// Note that JS gives us milliseconds, not seconds
let lastActivity = Date.now();
let now = Date.now();
let logoutAfter = 3600 * 1000;
let timer = setInterval(function () {
// PHP won't work, time() is rendered only once, on page load
// let now = <?php echo time(); ?>;
now = Date.now();
let delta = now - lastActivity;
console.log('New timer loop, now:', now, '; delta:', delta);
Now, even if there is a pause of 10s between iterations, delta will be the true measure of time elapsed since the page was loaded. So even if the user switches away to another tab, every time the loop runs, it will correctly track time, even if it doesn't happen every second.
So what does this mean in your case?
According to your report, JS is not running at all in the inactive tab. In that case, it can happen that the tab stays in the logged-in state, long past the time the user should have been logged out. However, assuming JS starts up again when you switch back the tab, the very first iteration of the loop will correctly calculate the time elapsed. If it is greater than your logout period, you will be logged out. So even though the tab stayed logged in longer than it should have, the user can't use it, since as soon as they switch to it they will be logged out. Note that "as soon" actually means "within 1 second plus the time it takes for the AJAX query to successfully log the user out".
In my testing, JS does not stop in an inactive Safari tab, but slows right down. In this case, it would mean that the user would be automatically logged out on the inactive tab, though not right at the time they should be. If the loop runs say every 8s, it could mean that the user would be logged out up to 7s later than they should have been. If iterations slow down even more, the delay can potentially be even more. Assuming JS starts up again as normal as soon as the user switches back the tab, behaviour will be exactly as above, the first iteration in that case will log them out.
EDIT
Here's simplified, complete code, and a JSFiddle showing it running and working.
jQuery(document).ready(function($) {
let lastActivity = Date.now();
let now = Date.now();
let logoutAfter = 3600 * 1000;
let timer = setInterval(function() {
now = Date.now();
let delta = now - lastActivity;
console.log('New timer loop, now:', now, '; delta:', delta);
if (delta > logoutAfter) {
alert('logout!');
}
}, 1000);
});

Javascript: How to speed up setTimeout function on website?

I am working on a website that asks me to complete a task, then once I complete that task it asks me to wait 1 hour before completing the next task.
What I am looking for here is to speed up the timer on this website instead of waiting for 1 hour.
How it works:
On Website I simply have to click on 'Roll' button then a timer start in descending order like (1:00)...(45:00)...(00:05) so on till it reach (00:00). Once it reach (00:00) it replace this timer to Roll button.
This timer only display Minutes and Second column.
It does not take computer time.
Changes I need:
Since it run in descending order or backward in seconds, I want to speedup this process so that instead of waiting for 1 hour I just have to wait for 20 or 30 minutes.
What I can't do:
Since this is a third party website so I cannot make changes in the website code I can only use browser console to run javascript code so I can override existing code on it.
Here is the Javascript for this timer:
<script>
$(function() {
$('#time_remaining').countdown({
until: +3600,
format: 'MS'
});
});
setTimeout(function() {
RefreshPageAfterFreePlayTimerEnds();
}, 3600 * 1000);
</script>
Looks like RefreshPageAfterFreePlayTimerEnds is global. So, you may try to override it like
var myTimeout = 3600; // 1 min
RefreshPageAfterFreePlayTimerEnds = function(speedUp) {
if(!speedUp) { // just to cancel "legal" call
return;
}
RefreshPageAfterFreePlayTimerEnds();
}
setTimeout(function(){ RefreshPageAfterFreePlayTimerEnds(true); }, myTimeout);
If you can't access to the website code, to change the code that doesn't allow you to reduce the time coding. You can change your IP address and use the website again.
If you have to sing in to use the website, forget, else you use another account and IP you will need to wait the time restricted to use again.

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

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

Categories

Resources