I have a countdown that every 30 seconds restarts the countdown.
var interval = 31000;
function reset() {
localStorage.endTime = +new Date + interval;
}
if( ! localStorage.endTime ) {
reset();
}
setInterval( function() {
var remaining = localStorage.endTime - new Date;
if( remaining >= 0 ) {
$('#timer').text( Math.floor( remaining / 1000 ) );
} else {
reset();
}
}, 100 );
I need to find a way to countdown across the whole server. So every viewer will experience the same countdown. Any ideas?
You can put that code in IIF in separate js file and inslude it on every page of your site and it should work cause local storage variables are available in every page in you site.
(function(){ /*... your code ...*/ })();
If you want to implement it on server side, so you can do it same way but instead of only setting value in local variable you also have to post it on server and retrieve when IIF invokes for first time (using AJAX).
(function(){ /*get variable end time and
in callback set it to local storage ... your code ...
in reset function post new value of end time on server */
})();
Related
I wrote a server and locally everything works great, but after deploy it to heroku setTimeout is executed instantly and without delay. Tried to deploy setTimeout(openCard(buttonIndex), 1000), timeoutID is global variable. Aclually don't what to do
const clickButton = (client, buttonIndex, time) => {
if(!(users[client.id])['isMaster']) {
if(room.teamTurn === (users[client.id])['team'] && !arrayToPlay[buttonIndex].isClicked && room.gameStarted && !globaleTime.MasterTurn) {
if(room[room.teamTurn].length > 1) {
addChosenWord(client,buttonIndex);
}
wordChosen = chosenWordCorrect() ? buttonIndex : null;
io.emit('wordChosen', wordChosen);
if(wordChosen !== null){
clearTimeout(timeoutID);
const date = new Date();
const localTimeOffset = date.getTimezoneOffset() * 60000;
const offset = date.getTime() - localTimeOffset - time;
timeoutID = setTimeout(()=>openCard(buttonIndex), 1000 + offset);
} else {
clearTimeout(timeoutID)
io.emit('room', room);
}
} else {
io.emit('buttonClicked', buttonIndex,client.id);
}
}
}
I noticed that you have in the code setTimeout(() => openCard(buttonIndex), 1000 + offset); but in your ask setTimeout(openCard(buttonIndex), 1000).
Check if the openCard is inside an arrow function in the setTimeout.
date.getTime() returns a time stamp based on the UTC time zone ref.
If the time argument comes from a date.getTIme call on the front end, the server should not be adjusting it for the time zone of the server.
Solution: remove local time zone adjustment on the server.
If the front end is sending the time based on local time, it would also need to notify the server what time zone it is in.
Solution: change the time sent from the client to use UTC time zone and remove local time zone adjustment on the server.
Most likely the code works locally because the server's time zone is the same as the client's, and deploying it to a server in another time zone breaks the current logic for calculating the timer millisecond count.
Assuming that time is in the UTC time zone, and that the objective is to open a card a second after clicking the button taking into account the time the request takes to reach the server, this example attempts to calculate a suitable delay:
if(wordChosen !== null){
clearTimeout(timeoutID);
// time in UTC zone:
let requestTime = Date.now() - time;
requestTime = Math.max( 20, requestTime); // not too low or negative
const responseFactor = 0; // 0 to ignore;
let delayTime = 1000 - requestTime - requestTime*responseFactor;
delayTime = Math.max(0, delayTime); // non negative
timeoutID = setTimeout(()=>openCard(buttonIndex), delayTime);
}
Set responseFactor to a number between 0 and 1 to include an estimate of the response time based on the request time.
I have a simple webworker which keeps the current time of timezone using setInterval
setInterval(() => {
userTimezoneTimestamp = userTimezoneTimestamp + 1000
postMessage(userTimezoneTimestamp);
}, 1000);
It works fine until I put my machine on sleep mode. When I restart the machine from sleep mode, the time which I get from this worker is older.
How can I restart my web worker only when the machine starts up from sleep mode?
There doesn't seem to be any DOM event letting us know about that event.
On my notebook Chrome does fire a non-standard orientationabsolutechange event, but I think not all notebooks have orientation aware hardware and already just Firefox on the same machine doesn't fire it.
But for what you want (an always up to date offset from an API served timestamp), you don't need a WebWorker at all, nor any timer, the computer comes with a good one and not only will it still be up to date after computer sleep, it will even be more precise than your interval which can suffer from time-drift.
All you need is to store the offset you got from your API and the computer's time you received it. Then you just need to get the difference between now and that time of reception and you can easily get your updated offset.
OP noted that they are afraid their users modify their computer's time to an earlier date, thus messing up with Date's values while the page is running. This can be detected. All it takes is to store the last value, and check if the difference with the current one is negative.
( async () => {
const offset_from_API = await getOffsetFromAPI();
const time_received = Date.now();
let last_time = time_received;
const getUpToDateOffset = () => {
const now = Date.now();
// Anti-Back-Time-Travelling check
// (it's a good idea to poll this method quite often too update `last_time`)
if( now - last_time < 0 ) {
throw new Error( 'cheater detected' );
}
last_time = now;
return offset_from_API + (now - time_received);
};
// to compare we will also use an incrementer
let incremented = offset_from_API;
setInterval( () => {
incremented += 1000;
console.clear();
console.log( 'incremented', incremented.toLocaleString( 'en-US' ) );
console.log( 'difference ', getUpToDateOffset().toLocaleString( 'en-US' ) );
}, 1000 );
} )();
function getOffsetFromAPI() { return 1234567; }
setInterval(() => {
/*handle userTimezoneTimestamp stuff every N miliseconds */
}, N);
setInterval(() => {
userTimezoneTimestamp = userTimezoneTimestamp + 1000
postMessage(userTimezoneTimestamp);
}, 1000);
I am working on a tool that require to measure certain amount of time (for example 2 minutes) since user interaction with a tool, this time also needs to be displayed for the user.
I started simple server loop for this reason:
setInterval(function() {
measure.time();
}, 10);
i created a code for to calculate:
this.reset = 0;
this.step = 100 // 10 ms server loop, adds 1 to reset === 100 is 1 second
this.time = function() {
if(this.reset === this.step) {
console.log('SPAWN', new Date());
this.reset = 0;
}
this.reset++;
};
this is the result of my approach:
The idea is to show clock for the user and update it every second, but it skips seconds rather often, what am i doing wrong?
I'm trying to store my script that counts numbers starting from 23,000 to always continue to appear it's "live" and always counting using Web Storage. I've tried implementing this and so far, I can't seem to get it to work. What would be the best solution to get this to work and function to always count even when refreshing, etc? I've included my JS Fiddle and code below. Any help is kindly appreciated!!
EDIT: To clarify.. I'm trying to have a "live" counter always going no matter what when you go to the page, refresh it, whatever. It's just always going and getting bigger no matter what just like my script does.. However, everytime I refresh it starts back at 23,000.
HTML
<span id="liveNumbers">23,000</span>
JS
if(typeof(Storage)!=="undefined")
{
setInterval(function(){
random = (Math.floor((Math.random()*2)+1));
var plus = Math.random() < 0.5 ? 1 : 1;
random = random * plus;
currentnumber = document.getElementById('liveNumbers');
var curnum = parseInt(currentnumber.innerHTML.replace(",",""));
document.getElementById('liveNumbers').innerHTML =
commaSeparateNumber(curnum + random);
}, 3000);
function commaSeparateNumber(val){
while (/(\d+)(\d{3})/.test(val.toString())){
val = val.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");
}
return val;
}
}
else
{
// Sorry! No Web Storage support..
}
Here's my attempt: fiddle
The logic:
On first visit (no localStorage data) the counter is reset to 23000.
Counter runs while page is open.
When closing the page, the current counter value is stored together with the current timestamp (lastSessionEnd).
When user loads the page again, the time that has passed since he closed the page is translated into interval cycles which are passed to the randomRange function and added to the stored counter from the last session.
Here's the code:
if(window.localStorage) {
//configs
var updateInterval = 3000; //ms
function randomRange() {
return Math.floor(Math.random()*3)+1; // [1..3] range
}
var counter = +localStorage.getItem('counter');
if (!counter) { //first load
counter = 23000;
} else { //simulate randomness that would have happened while the user was away from the page
var lastSessionEnd = +localStorage.getItem('lastSessionEnd');
for(var l = Math.floor((getUnixTimeStamp() - lastSessionEnd)*1000/updateInterval); l--;) {
counter += randomRange();
}
}
var liveNumbers = document.getElementById('liveNumbers'); //cache DOM query
function refreshDisplay() {
liveNumbers.innerHTML = commaSeparateNumber(counter);
}
refreshDisplay();
setInterval(function() {
counter += randomRange();
refreshDisplay();
}, updateInterval);
function commaSeparateNumber(val) {
while (/(\d+)(\d{3})/.test(val.toString())){
val = val.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");
}
return val;
}
function getUnixTimeStamp() {
return Math.floor(Date.now()/1000);
}
window.addEventListener('beforeunload', function() {
localStorage.setItem('counter', counter);
localStorage.setItem('lastSessionEnd', getUnixTimeStamp());
});
} else {
// Sorry! No Web Storage support..
}
NOTE: this is not perfect, here are the caveats:
As it is done purely in the front-end, it is easily hackable by manipulating the localStorage. Don't use this for important stuff.
As it uses the localStorage API, if the user opens the page in more than one browser (or more than one computer/device), each one will have a different counter. Also, cleaning all personal data will reset the counter.
Finally, there's an interval cycle rounding error, it doesn't account for interrupted interval cycles. E.g. the user closes the page midway through an interval cycle, the next time he opens the page that half-cycle will be discarded and a new one starts. I believe this is a small detail which would take more effort to fix than it's worth, but I'll leave that decision and effort to you.
I've built a simple JavaScript-based timer for a mobile webapp; for the sake of example:
var a = 0;
setInterval(function() {
console.log('a', a);
a++;
}, 1000);
This runs just fine in both Mobile Safari and Android Browser. It will log to console every second and increment the value of a accordingly. (Okay, Android Browser doesn't have console.log support, but let's assume it does.)
The issue: if the screen times out (i.e. user stopped interacting with the page), the setInterval function pauses. It resumes when the user turns on their screen again. This won't work for me as I need timer to keep running.
The questions: Is there a way to prevent the setInterval function from pausing when the screen times out? If not, is it possible to prevent the screen from timing out? Any other alternatives?
Thanks in advance!
Basically, no. The phone enters a sleep state to save battery when the screen times out. Since you can't see anything anyway, a large number of processing tasks are stopped. Similar things will occur when you change tabs/windows (the page is unloaded from memory). Right now there is no way to request that the device stays on from a web application. Future support in Android for accessing hardware may provide this functionality, but personally I doubt it.
If you need always running support, you'll need to write native applications for both systems (plus on Android it can always run).
You can use the Page Visibility API to detect when the page is hidden or visible. For example, if the user navigates away from the browser and back again or the screen turns off and on.
I used this answer to help create by solution.
You will need to store the time you set your interval. Then when the visibilityChange event listener indicates the document is visible again, you can calculate the amount of time that has passed since you first started the interval and update your data as needed.
In my case I was creating a count down timer in my Angular2 project. My page was running on an iPad and the timer was pausing whenever the screen turned off. So I added the event listener in my ngOnInit(). Then when the screen turned back on I could update my timer to show the correct time left since it was started.
I am using the moment npm package to handle my date time.
The timerInfo object is a class variable that gets updated by the interval callback. self.zone.run() is used to propagate the changes to the DOM so that the updated time gets displayed.
Written in typescript:
private timerInfo:{
days?:number,
hours?:number,
minutes:number,
seconds:number
};
private startTime:Moment = moment();
private timerDuration:number = 20; // in minutes
private timerHandle:any;
ngOnInit() {
this.setVisibilityListener();
}
private setVisibilityListener():void {
var self = this;
var hidden, visibilityState, visibilityChange;
if (typeof document.hidden !== "undefined") {
hidden = "hidden";
visibilityChange = "visibilitychange";
visibilityState = "visibilityState";
}
var document_hidden = document[hidden];
document.addEventListener(visibilityChange, function () {
if (document_hidden != document[hidden]) {
if (document[hidden]) {
// Document hidden
console.log("document hidden");
} else {
// Document shown
console.log("document shown; setCountDownTimer()");
self.setCountDownTimer();
}
document_hidden = document[hidden];
}
});
}
private setCountDownTimer():void {
var self = this;
if (self.startTime) {
var startMoment = moment(self.startTime);
var endMoment = startMoment.add(self.timerDuration, "minutes");
console.log("endMoment: ", endMoment.toISOString());
self.clearTimer();
var eventTime = endMoment.unix();
var currentTime = moment().unix();
var diffTime = eventTime - currentTime;
var duration = moment.duration(diffTime * 1000, 'milliseconds');
var interval = 1000;
// if time to countdown
if (diffTime > 0) {
self.timerHandle = setInterval(() => {
self.zone.run(() => {
var diff = duration.asMilliseconds() - interval;
if (diff < 0) {
self.clearTimer();
self.timerComplete();
} else {
duration = moment.duration(duration.asMilliseconds() - interval, 'milliseconds');
self.timerInfo = {
days: moment.duration(duration).days(),
hours: moment.duration(duration).hours(),
minutes: moment.duration(duration).minutes(),
seconds: moment.duration(duration).seconds()
};
// console.log("timerInfo: ", JSON.stringify(self.timerInfo));
}
});
}, 1000);
} else {
self.timerComplete();
}
}
}
private clearTimer():void {
if (this.timerHandle) {
clearInterval(this.timerHandle);
this.timerHandle = null;
}
}