Why does setInterval not increment my clock properly in JavaScript? - javascript

I want to display the actual time in New York. I have a html div:
<div id="time"></div>
and also - I have a php script that returns the actual time:
<?php
date_default_timezone_set('UTC');
echo time();
?>
and it does it as a timestamp.
Now, I've created a js script:
var serverTime;
moment.tz.add('America/New_York|EST EDT|50 40|0101|1Lz50 1zb0 Op0');
function fetchTimeFromServer() {
$.ajax({
type: 'GET',
url: 'generalTime.php',
complete: function(resp){
serverTime = resp.responseText;
function updateTimeBasedOnServer(timestamp) { // Take in input the timestamp
var calculatedTime = moment(timestamp).tz("America/New_York");
var dateString = calculatedTime.format('h:mm:ss A');
$('#time').html(dateString + ", ");
};
var timestamp = serverTime*1000;
updateTimeBasedOnServer(timestamp);
setInterval(function () {
timestamp += 1000; // Increment the timestamp at every call.
updateTimeBasedOnServer(timestamp);
}, 1000);
}
})
};
fetchTimeFromServer();
setInterval(function(){
fetchTimeFromServer();
}, 5000);
and the idea behind it is that I want to fetch the data from server, display it on my webpage, then increment it every second for five seconds and then fetch the time from the server again (to keep consistence with time on the server). And later on - continue with doing so, fetching the time, incrementing it for 5 seconds, fetching it again, etc.
It works... almost. After the webpage stays open for some time I can see the actual time, but it 'blinks', and I can see that it shows different times - it's hard to explain, but it looks like there is some time already in that div and new time tries to overlay it for each second. Seems like the previous time (content of this div) is not removed... I don't know how to create a jsfiddle with a call to remote server to fetch time from php, so I only have this information pasted above :(
What might be the problem here?

Since javascript is single threaded, setInterval may not acutally run your function after the delay. It adds the function to the stack to be run as soon as the processor is ready for it. If the processor has other events in the stack, it will take longer than the interval period to run. Multiple intervals or timeouts are all adding calls to the same stack for processing. To address this, you could use HTML5 web workers or try using setTimeout recursively.
Here is a good read on web workers: https://msdn.microsoft.com/en-us/hh549259.aspx

Related

ERR_INSUFFICIENT_RESOURCES ajax code for REAL-TIME CLOCK

I created a real-time clock that updates every second. When I run it locally I see no errors. However when I uploaded it to my web host I saw a lot of error messages in the console every time the AJAX code runs.
I think that's the reason why web host suspended my site, telling that my site has performed too many requests.
Here is my code:
$(document).ready(function() {
setInterval(function() {
$('#time').load('timewithdate.php')
}, 1000);
setInterval(function(){
$('#time2').load('time.php')
}, 1000);
})
I'm not sure what kind of answer you are looking for. Your code is a certain way to kill a server: it's making 2 calls to the server every second for each client (read more about DDOS). There is no need to make a server call, just use javascript to get the current time and format it the way you want. You can have something like this:
$(document).ready(function() {
setInterval(function() {
let curTime = new Date();
let date = curTime.toLocaleDateString();
let time = curTime.toLocaleTimeString();
$('#time').text(date + " " + time);
$('#time2').text(time);
}, 1000);
})
The error is because you have effectively DDOS'd your own server with 2 requests every second * number of concurrent clients. I would strongly suggest you remove these AJAX requests and perform the countdown on the client side.
If you're trying to keep the clocks in sync with server time, get the time from the server when the page loads, then add seconds to it on the client side. Do not use AJAX for this, and do not use AJAX polling in future. It's an anti-pattern.

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

Is it possible to know how long a user has spent on a page?

Say I've a browser extension which runs JS pages the user visits.
Is there an "outLoad" event or something of the like to start counting and see how long the user has spent on a page?
I am assuming that your user opens a tab, browses some webpage, then goes to another webpage, comes back to the first tab etc. You want to calculate exact time spent by the user. Also note that a user might open a webpage and keep it running but just go away. Come back an hour later and then once again access the page. You would not want to count the time that he is away from computer as time spent on the webpage. For this, following code does a docus check every 5 minutes. Thus, your actual time might be off by 5 minutes granularity but you can adjust the interval to check focus as per your needs. Also note that a user might just stare at a video for more than 5 minutes in which case the following code will not count that. You would have to run intelligent code that checks if there is a flash running or something.
Here is what I do in the content script (using jQuery):
$(window).on('unload', window_unfocused);
$(window).on("focus", window_focused);
$(window).on("blur", window_unfocused);
setInterval(focus_check, 300 * 1000);
var start_focus_time = undefined;
var last_user_interaction = undefined;
function focus_check() {
if (start_focus_time != undefined) {
var curr_time = new Date();
//Lets just put it for 4.5 minutes
if((curr_time.getTime() - last_user_interaction.getTime()) > (270 * 1000)) {
//No interaction in this tab for last 5 minutes. Probably idle.
window_unfocused();
}
}
}
function window_focused(eo) {
last_user_interaction = new Date();
if (start_focus_time == undefined) {
start_focus_time = new Date();
}
}
function window_unfocused(eo) {
if (start_focus_time != undefined) {
var stop_focus_time = new Date();
var total_focus_time = stop_focus_time.getTime() - start_focus_time.getTime();
start_focus_time = undefined;
var message = {};
message.type = "time_spent";
message.domain = document.domain;
message.time_spent = total_focus_time;
chrome.extension.sendMessage("", message);
}
}
onbeforeunload should fit your request. It fires right before page resources are being unloaded (page closed).
<script type="text/javascript">
function send_data(){
$.ajax({
url:'something.php',
type:'POST',
data:{data to send},
success:function(data){
//get your time in response here
}
});
}
//insert this data in your data base and notice your timestamp
window.onload=function(){ send_data(); }
window.onbeforeunload=function(){ send_data(); }
</script>
Now calculate the difference in your time.you will get the time spent by user on a page.
For those interested, I've put some work into a small JavaScript library that times how long a user interacts with a web page. It has the added benefit of more accurately (not perfectly, though) tracking how long a user is actually interacting with the page. It ignore times that a user switches to different tabs, goes idle, minimizes the browser, etc.
Edit: I have updated the example to include the current API usage.
http://timemejs.com
An example of its usage:
Include in your page:
<script src="http://timemejs.com/timeme.min.js"></script>
<script type="text/javascript">
TimeMe.initialize({
currentPageName: "home-page", // page name
idleTimeoutInSeconds: 15 // time before user considered idle
});
</script>
If you want to report the times yourself to your backend:
xmlhttp=new XMLHttpRequest();
xmlhttp.open("POST","ENTER_URL_HERE",true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
var timeSpentOnPage = TimeMe.getTimeOnCurrentPageInSeconds();
xmlhttp.send(timeSpentOnPage);
TimeMe.js also supports sending timing data via websockets, so you don't have to try to force a full http request into the document.onbeforeunload event.
The start_time is when the user first request the page and you get the end_time by firing an ajax notification to the server just before the user quits the page :
window.onbeforeunload = function () {
// Ajax request to record the page leaving event.
$.ajax({
url: "im_leaving.aspx", cache: false
});
};
also you have to keep the user session alive for users who stays long time on the same page (keep_alive.aspxcan be an empty page) :
var iconn = self.setInterval(
function () {
$.ajax({
url: "keep_alive.aspx", cache: false });
}
,300000
);
then, you can additionally get the time spent on the site, by checking (each time the user leaves a page) if he's navigating to an external page/domain.
Revisiting this question, I know this wouldn't be much help in a Chrome Ext env, but you could just open a websock that does nothing but ping every 1 second and then when the user quits, you know to a precision of 1 second how long they've spent on the site as the connection will die which you can escape however you want.
Try out active-timeout.js. It uses the Visibility API to check when the user has switched to another tab or has minimized the browser window.
With it, you can set up a counter that runs until a predicate function returns a falsy value:
ActiveTimeout.count(function (time) {
// `time` holds the active time passed up to this point.
return true; // runs indefinitely
});

I need to be able to hide the place order button in Magento checkout if time x is reached

I need to be able to hide the “Place Order” button if a deadline is reached during the checkout phase. In this store customers are picking routes for their orders and then they pick up their orders from the route.
When customer picks a route a cookie with a deadline time is set, i need to compare this cookie to current time in real-time during checkout progress to make sure that customers cant place orders to a route that has been closed.
I have the time compare working in PHP but it only gets the current time on page load, so if someone spends too much time in the checkout it might be a problem. I have tried ajax scripts and placed them inside the button.phtml but for some reason they dont seem to work. I am sure this is something related to magento’s theme/file structure that i am not getting.
This is my solution so far. Its very straightforward but my problem now is that i cant figure out how to get the current time from the server as javascript is client side it uses user's settings for clock. Its surprisingly difficult to do this one simple thing. I think i can figure it out soon enough.
This code resides in button.phtml
<script>
document.getElementById('deadline_reached').style.visibility = 'hidden';
function validateDeadline() {
<?php
date_default_timezone_set('Europe/Helsinki');
$deadlineTimeHuman=$_COOKIE["deadlineDate"];
$deadlineTimeUnixTimestamp = strtotime($deadlineTimeHuman);
?>
// Current date
var timeNowCompare = Math.round(new Date().getTime() / 1000) //Current date is changed to unix timestamp without milliseconds os it matches with php's timestamp format.
//Cookie's deadline time which has been changed to timestamp is loaded into variable.
var deadlineDate = "<?php echo $deadlineTimeUnixTimestamp ?>";
if (timeNowCompare < deadlineDate) // What happens when we are in schedule.
{
alert("Now < Deadline Order can be placed \n"+"n:"+ timeNowCompare +"\nd:"+deadlineDate); // This is for debugging
}
else if (timeNowCompare > deadlineDate) // What happens when current time is higher than deadline.
{
alert("Now > Deadline Order cant be placed \n"+"n:"+ timeNowCompare +"\nd:"+deadlineDate); // This is for debugging
document.getElementById('place_order_btn').style.visibility = 'hidden';
document.getElementById('deadline_reached').style.visibility = 'visible';
}
}
window.setInterval(function(){
validateDeadline();
}, 10000);
</script>

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