Using requestAnimationFrame as a Timer - javascript

This is a multi-player "guess the word" turn-based game I am working on.
I have a page that is supposed to count down from a given number. If a word-guess is submitted before the time limit, a SignalR hub method is called. If 0 is reached, a different SignalR hub method is called. In both cases, the hub method sends a message back out to all relevant clients (in a group).
Someone recommended, instead of a JS timer, I can use requestAnimationFrame which updates every 17 milliseconds.
So here is what requestAnimationFrame code I have in the Play.aspx file...
function turnTimerOn() { //called by server code as a startup script
var timerStep = '<%=Application(Session("GameID") & "_TimeLimit")%>';
timerStep = Number(timerStep);
var startT = Date.now();
var timedifference = 0;
var countdown = document.getElementById('SecondsLeft');
requestAnimationFrame(function anim() {
requestAnimationFrame(anim);
startT = startT || Date.now();
countdown.innerHTML = timerStep - ((Date.now() - startT) / 1000 | 0);
timedifference = (Date.now() - startT) / 1000 | 0;
if (timedifference >= timerStep) {
startT = Date.now();
timedifference = 0;
countdown.innerHTML = "";
TomatoHubProxy.invoke('NextPlayer', '<%=Session("username")%>', '<%=Session("GameID")%>');
}
});
}
The return message from the SignalR hub causes execution of turnTimerOn() again, and this is meant to start the countdown again for the current player.
The count down seems to work but has a bug where it reaches a low number like 3 or 4 and then either resets the count down or invokes the Hub method (it's hard to tell what is happening). It is supposed to invoke the Hub method when the count down reaches 0 (so that the game can move to the next player)
I should also mention the Play.aspx page is never re-loaded. All updates happen automatically (thanks to SignalR)
Please help.

Related

Trigger client side javascript based on datetime

I have an event_start time and I want to run a script 10 seconds after that event start but can't figure out how to trigger the script.
const date = Date().toString();
const currentDateParse = Date.parse(date);
const trigger = Date.parse(startTime) + 1000 * 10
Then this is how I'm attempting to trigger the script but it's not working. How do I start my function when the currentDateParse is equal to trigger. Or, put more simply, 10 seconds after the event starts.
if (currentDateParse = trigger)
<function code underneath works already>
Calculate the time difference between now and when you want to trigger the script, and use setTimeout() to call it then.
const timediff = trigger - new Date().getTime();
setTimeout(function() {
// do what you want
}, timediff);
Try this (explanations below):
let startTime = "18:00";
let currentTime = new Date();
let target = new Date().parse(startTime);
let timeout = target - currentTime + 1000 * 10; // how long should it wait till the functions is executed
function runTenSecAfterEvent() {
// do something here
}
setTimeout(runTenSecAfterEvent, timeout);
Explanations
After you calculated target (the start time) and currentTime, you need to know the time difference between them (timeout), so target minus currentTime. After that, you add the 10000ms.
And then you define the function which will be executed 10 seconds after the event occurred (runTenSecAfterEvent()).
Finally, you create a timeout.

the function setTimeout does not work on heroku

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.

node.js setInterval vs real time

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?

wavesurfer get elapsed time in real time

I'm using katspaugh's waveSurfer library for playing sound files.
I wrote code to show 'elapsed time / total time' in this way.
waveSurfer.on('play', function() {
$scope.getCurrentDuration();
});
$scope.getCurrentDuration() is a function to transform floating-type variable to string-type variable, like below:
$scope.getDuration = function() {
if ($scope.waveSurferReady) {
var time = waveSurfer.getDuration(); // get duration(float) in second
var minute = Math.floor(time / 60); // get minute(integer) from time
var tmp = Math.round(time - (minute * 60)); // get second(integer) from time
var second = (tmp < 10 ? '0' : '') + tmp; // make two-figured integer if less than 10
return String(minute + ':' + second); // combine minute and second in string
}
else {
return 'not ready yet'; // waveSurfer is not ready yet
}
};
But the problem is,
in this part:
waveSurfer.on('play', function() ...)
the callback function execute only once.
I expect the callback function called periodically, but it executes only once, so as the result, elapsed time is shown only at the start time.
How can I solve this?
Looking into the source, I've found the audioprocess event paired with html5 timeupdate event.
Try it out.
waveSurfer.on('audioprocess', function() {
// do stuff here
});

Gmail-like countdown timer not working

I've been trying to recreate a countdown timer similar to the one gmail uses that when you get disconnected from internet. The ajax request fails then it begin a short countdown then makes another ajax request and if it fails again then begin a longer countdown and so forth. Once a determined countdown value is reached (lets say 1 minute), the countdown is maintained at 1 minute until the internet connection is recovered or the servers comes back.
I don't want to use a plugin because this code will be embedded in a micro-controller that has limited space and also prefer not to place it as external file for practical reasons, even though jQuery library will already be external.
Everything should trigger onload, and continue automatically (i.e. no control inputs will be used).
So far I've developed code that does what I want if the ajax request succeeds or fails immediately but if there is a latency on the ajax request status (as for example having the server down) the browser won't produce an immediate result and my code fails.
I know what I stated above because I actually took the server down and was been help by firebug on Mozilla Firefox to see that the ajax result (success or failure) wasn't triggered immediately but keep waiting for several seconds.
Help please!
html code:
<div id='tempFail' ></div>
jQuery code:
$(document).ready(function()
{
//do when DOM ready - http://api.jquery.com/ready/
var timerSpeed = [1000, 5000, 10000, 20000, 30000, 60000];
// current time in ms since 1/1/1970, plus the initial reload interval
var end = (new Date).getTime() + timerSpeed[1];
var n=0;
var m=0;
setInterval(function()
{
var now = (new Date).getTime();
// Current time left to re-load in seconds, sometimes it goes to negative values, see below
var secLeft = Math.floor(( end - now ) / 1000);
// if secLeft is negative multiply by zero...equal to secLeft=0, this will produce an error of one second approximately
var timeToload = (secLeft < 0) ? secLeft * 0 : secLeft;
if (n!=0)
{
//check for failed or delayed request\n\
$('#tempFail').html('Failed or delayed response. Will auto load in: '+timeToload+ ' seconds!');
}
if( (secLeft)<=0)// if reload time is reached do
{
if (m==0)//used to prevent multiple continue reloads when ajax request status is not yet defined
{
m=1;
$.getScript('script_vars.js').done(function (data)
{
//if request succeeded
m=0;
n = 0;
end = (new Date).getTime() + timerSpeed[1];
// Time to load after the initial interval set above
$('#tempFail').html('');
//other code on success here
})
.fail(function()
{
//if request failed
m=0;
n ++;
if(n==6) n=5;
switch(n){ //timer delay for failed request\n\
case 1:
end = (new Date).getTime() + timerSpeed[1];
break;
case 2:
end = (new Date).getTime() + timerSpeed[2];
break;
case 3:
end = (new Date).getTime() + timerSpeed[3];
break;
case 4:
end = (new Date).getTime() + timerSpeed[4];
break;
case 5:
end = (new Date).getTime() + timerSpeed[5];
break;
}
});
}
}
}, 1000);
});
You asked for an example so I've written the following, you may want to wrap the contents of the function within another function so you can repeat it/don't have to worry about namespaces/etc. Didn't test so don't expect bug free!
Using window.setTimeout for every action, separated each stage into it's own function so code paths can more easily be followed.
$(document).ready(function () { // http://api.jquery.com/ready/
var $tempFail = $('#tempFail'),
delay = [1000, 5000, 10000, 20000, 30000, 60000],
delay_index = 0,
delay_ends = 0,
inform_user_ref = null,
inform_user = function inform_user() {
var now = (new Date).getTime(),
delta; // for difference, calculate later
if (delay_ends > now) { // if we're waiting for a retry
delta = Math.floor((delay_ends - now ) / 1000); // calculate time to wait
$tempFail.html('Failed or delayed response. Will auto load in: '+delta+ ' seconds!'); // let people know
window.setTimeout(inform_user, 200); // loop countdown timer
// can fast refresh this as it's just a countdown
}
},
get_success = function () {
$tempFail.html('');
// .. code on success
},
get_fail = function () {
delay_index < 5 && ++delay_index; // increment delay_index
get_initialise(); // retry
window.clearTimeout(inform_user_ref); // remove any old countdown timer
inform_user_ref = inform_user(); // and display new countdown
},
get_try = function () {
$.getScript('script_vars.js')
.done(get_success)
.fail(get_fail);
},
get_initialise = function () {
delay_ends = (new Date).getTime() + delay[delay_index];
window.setTimeout(get_try, delay[delay_index]); // retry
};
get_initialise(); // initial
});
Wow! Mr Paul S. your code was crazy good. I just made a couple of adjustments to have it perfectly working as I need it.
Added the following on ajax success:
delay_index = 0; //reset delay_index
get_initialise(); // retry
so I keep the code running every 5 seconds if everything goes ok.
2.
Added two new variables: let_cntDwn_end and ajax_rqst_status to avoid countdown number jumping (to let countdown finish before beginning the next one ) and to display a message while the ajax request haven't given any result respectively.
Here is the new code:
$(document).ready(function(){ //do when DOM ready - http://api.jquery.com/ready/
var $tempFail = $('#tempFail'),
delay = [5000, 5000, 10000, 20000, 30000, 60000],
delay_index = 0,
delay_ends = 0,
inform_user_ref = null,
let_cntDwn_end = 0,
ajax_rqst_status = 0, //ajax success or failure triggered
inform_user = function inform_user() {
var now = (new Date).getTime(),
delta; // for difference, calculated later
if (delay_ends > now) { // if we're waiting for a retry
let_cntDwn_end = 1;
delta = Math.floor((delay_ends - now ) / 1000); // calculate time to wait
if (ajax_rqst_status==0){
$tempFail.html('Failed response. Will auto load in: '+delta+ ' seconds!'); // let people know
window.setTimeout(inform_user, 900); // loop countdown timer
// can fast refresh this as it's just a countdown
}
}
else {let_cntDwn_end = 0; get_try();}
},
get_success = function () {
ajax_rqst_status =0;
$tempFail.html('');
// .. code on success
delay_index = 0; //reset delay_index
get_initialise(); // retry
},
get_fail = function () {
ajax_rqst_status =0;
delay_index < 5 && ++delay_index; // increment delay_index
get_initialise(); // retry
window.clearTimeout(inform_user_ref); // remove any old countdown timer
inform_user_ref = inform_user(); // and display new countdown
},
get_try = function () {
if (let_cntDwn_end == 0){
ajax_rqst_status=1;
$tempFail.html('Waiting for Ajax request success or failure'); // let people know
$.getScript('script_vars.js')
.done(get_success)
.fail(get_fail);
}
},
get_initialise = function () {
delay_ends = (new Date).getTime() + delay[delay_index];
window.setTimeout(get_try, delay[delay_index]); // retry
};
get_initialise(); // initial
});
There's also JS lib which handles this for you by monitoring ajax requests.
https://github.com/HubSpot/offline

Categories

Resources