Heartbeat method in Meteor - javascript

I have a Meteor application that, for every second while the user is holding a button, decrements a value on a post by 1. There are many many buttons, and each should only affect itself.
If one person is holding the button, it will go down by a rate of one per second. If two people (on different clients) then by two per second.
The exact moment at which it is updated in the database is unnecessary, only that the client should see the counter decrement by a variable amount each second.
So to trigger the method I used the following
Template.button.onRendered(function() {
var data = this.data;
Meteor.setInterval(function() {
if(isClicked) {
Meteor.call('heartbeat', data);
}
}, 1000);
});
which calls a client/server method
'heartbeat': function(button) {
Buttons.update({_id: button._id}, {$inc: {life: -1}});
if(button.life <= 1) {
console.log('REMOVE DEAD BUTTONS');
Buttons.remove({life: {$lte: 0}});
}
}
And causes weird activity when latency comes into play. When I click on a button, its life goes down predictably at 1hz on my client. On a different client, its life might stand still for several seconds, and then decrement in chunks of 2 to 4 until it catches up. It is supposed to just jump to the new value as soon as it hears from the server that it's been updated, rather than ramping down.
But since there is no code to make it ramp, I am led to believe that there is something more fundamentally wrong in play. There are three states a button goes through:
Client A decrements value -> Server decrements value -> Client B reads value
The problem seems to between Client A and the server, so perhaps setting an interval to call a method every second is the bottleneck here that I need to fix, but I'm not sure of any elegant solutions. If instead of sending each update, I adopted a dead reckoning approach and send the start and ends and had the server interpolate on each second in between, that might alleviate some issues, but I don't know if I'm going to run into more unexpected issues along that line.
Is there a significantly better way of decrementing a counter on the second every second for every client reactively?

An alternate approach:
When the user presses the button (catch this in a template event handler), make a Meteor.call() to the server and have the server decrement every second.
When the user releases the button, make another Meteor.call() to cancel the decrementing. You'll need to keep a handle to the setInterval so you can cancel it.
This way you'll remove the client->server lag for doing the updates.

Related

react-Countdown Timer (minor out of sync) problem after refreshing the screen

I have an auction site in MERN stack with socket.io and i seem to have this unsolvable problem which i think is related to browsers and basic JS
Flow:
whenever a product is added by admin, socket broadcasts it with all
of details (including time )to all clients.
the clients can bid on them and stuff.
if a user refreshes the screen , it requests the socket for latest
product time and starts countdown from that time.
Everything is fine except some times react-countdown is lagging 0.5 second to 1 second behind whenever page is refreshed (please note that problem does not occur when opening same auction on new tab)
Note: i have also tried self made Countdown timer using setInterval but the problem does not go away
I am seeking assistance with this problem and am willing to compensate someone for their time and efforts to work with me directly to resolve it. Any help would be greatly appreciated.
Using setInterval and setTimeout means you are at the mercy of the browser. Browsers will often slow down the execution of these if some other process is happening and return to it once that's done, or if you switch away to another tab, they will purposefully reduce the tick rate. The level of accuracy you require is not easily achieved.
Firstly, I would suggest that getting the time it ends, then finding the difference between then and now, and counting down from this value with 1s increments will aggravate this problem. If each "tick" is off by even a small amount, this will accumulate into a larger error. This is probably what the library is doing by default. It may have also been what you were doing when you made your own timer, but I'd need to see it to confirm.
Instead, you need to store the time it ends passed from the socket (or keep it in scope like below) and on each "tick", work out the difference between then and now, every single time.
You could do this by using react-countdown in controlled mode and doing this logic in the parent.
I've made up a function here which would be the thing that receives the time from the socket -- or it's called from it. Its pseudo-code.
const timer = useRef(null)
const [timeLeft, setTimeLeft] = useState(null) // In seconds
const handleSocketReceived = (({endTime}) => {
timer.current = setInterval(() => {
const newTimeLeft = endTime - Date.now() // Pseudo code, depends on what end time is, but basically, work out the diff
setTimeLeft(newTimeLeft)
}, 100) // Smaller period means more regular correction
}, [])
// ...
useEffect(() => {
return () => clearInterval(timer.current)
}, [])
// ...
<Countdown date={timeLeft} controlled={true} />

Is there better approach for auto background saving in web?

I have this code and having a hard time figuring out what to do, wherein the UI there is a save button and an interval saving. if the interval saving is in progress then the user hits the save button, the same request on the button should deny this request. for now, in my code, I subscribe on this BehaviourSubject variable to see it there in progress then execute request if false.
is there any mechanism or another best approach for this rather than flagging? because I face an issue, what if the interval saving and saving button is triggered and same second/moment/time?
Observable.interval(180000).subscribe((data) => {
if (!this.saveService.getSavingProgress()) {
this.saveService.autoSave().subscribe((status) => {
}
}
Use the throttle operator provided by RxJS https://rxjs-dev.firebaseapp.com/api/operators/throttle

Second jQuery.post request finishing before first one. How to avoid this?

I'm trying to implement a search bar like facebook for my website users. While you type the name, some results are displayed.
To avoid sending a request for each button pressed, i've setup a timeout. The idea is: if i'm going to type jack It doesn't make sense to search for j, ja, jac, jack, but it's better to wait for the users finishes typing before sending any request.
Once the request is sent and completes, a div called mydiv is filed with results (the request response). If after some time long enough i type another letter, another request is sent and mydiv is filled with new results.
The idea comes from this question.
Here is my simplified implementation.
var thread = null;
$('#search-user').keyup(function() {
clearTimeout(thread);
name = $('this').val(); //i get the name just typed
thread = setTimeout(function(){
get_data_with_ajax_and_put_it_in_mydiv(name);
}, 200);
});
function get_data_with_ajax_and_put_it_in_mydiv(name){
$.post()....
/*Do some stuff*/
}
As you can see, after a 200ms timeout the function get_data_with_ajax_and_put_it_in_mydiv(); is called. This function, emptys mydiv before putting new data. It works almost always, but sometimes a strange bug occurs.
I'm going to explain where i think the problem is, by providing a real example. Let us assume that get_data_with_ajax_and_put_it_in_mydiv() takes from 50ms to 1000ms to complete, depending on various factors (network speed, congestion, etc...).
At t=0 i type jac and i stop typing.
At t=200 the timeout expires and get_data_with_ajax_and_put_it_in_mydiv() runs. In this case it takes 1000ms to run
At t=210, because i'm a slow writer, i type the letter k of jack. mydiv is empty because
the first request has not yet completed.
At t=410 the timeout for the letter k epires and a second request
is sent. This time, the function takes 100ms to run.
At t=510 the second request finishes and i get the results for jack.
At t=1200 the first request finishes and i get the results for jac.
ERROR
As you can see due to impredictability of time elaped by ajax requests i got a wrong result. mydiv is filled with jac results and not jack.
SOLUTION
The first solution that came to my mind is the following: every time i call get_data_with_ajax_and_put_it_in_mydiv() i have to stop any previous request still running. How can i do that? If not possible, how can i fix this error?
Why not use a debounced function to handle the ajax call?
http://benalman.com/code/projects/jquery-throttle-debounce/examples/debounce/
You should use the Promise interface and use the 'done' method to chain these two operations sequentially
http://api.jquery.com/jquery.post/

Prevent JavaScript Timer Re-entry

My ASP.NET MVC page uses JavaScript/jQuery to poll my database every second.
This is working but I want to make sure that, if there is a delay, my timer handler won't get called again before it has returned.
In there any trick to this other than storing the timer ID in a global variable, clearing the timer in my handler, and restarting it when my handler is done.
NOTE: I realize every second seems frequent but this code is polling my server after submitting a credit card payment. Normally, it will only run for a second or so, and I don't want the user to wait any longer than necessary.
Polling every second? That's quite heavy!
That aside, you won't have this issue when setTimeout is used instead of setInterval. The latter ensures that a piece of code is run x times given a interval, while the former ensures that there's a delay of at least x milliseconds.
function some_poller() {
$.ajax({
url: '/some_page',
success: function() {
setTimeout(some_poller, 1000);
},
error: function() { // Also retry when the request fails
setTimeout(some_poller, 1000);
}
});
}
// Init somewhere
some_poller();
Not really, although I wouldn't recommend using a global variable. Stick it inside some function.
But are you really sure you need to poll every second? That's an extremely chatty interface.
In my personal experience a "global", (inside of the root function), variable works very well in this instance so that you can control when to clear and restart. If the response is really as quick as you say, this shouldn't cause too much overhead, (clearing/resetting), and will allow to account for these type of situations.

Can I interrupt javascript code and then continue on a keystroke?

I am porting an old game from C to Javascript. I have run into an issue with display code where I would like to have the main game code call display methods without having to worry about how those status messages are displayed.
In the original code, if the message is too long, the program just waits for the player to toggle through the messages with the spacebar and then continues. This doesn't work in javascript, because while I wait for an event, all of the other program code continues. I had thought to use a callback so that further code can execute when the player hits the designated key, but I can't see how that will be viable with a lot of calls to display.update(msg) scattered throughout the code.
Can I architect things differently so the event-based, asynchronous model works, or is there some other solution that would allow me to implement a more traditional event loop?
Am I making sense?
Example:
// this is what the original code does, but obviously doesn't work in Javascript
display = {
update : function(msg) {
// if msg is too long
// wait for user input
// ok, we've got input, continue
}
};
// this is more javascript-y...
display = {
update : function(msg, when_finished) {
// show part of the message
$(document).addEvent('keydown', function(e) {
// display the rest of the message
when_finished();
});
}
};
// but makes for amazingly nasty game code
do_something(param, function() {
// in case do_something calls display I have to
// provide a callback for everything afterwards
// this happens next, but what if do_the_next_thing needs to call display?
// I have to wait again
do_the_next_thing(param, function() {
// now I have to do this again, ad infinitum
}
}
The short answer is "no."
The longer answer is that, with "web workers" (part of HTML5), you may be able to do it, because it allows you to put the game logic on a separate thread, and use messaging to push keys from the user input into the game thread. However, you'd then need to use messaging the other way, too, to be able to actually display the output, which probably won't perform all that well.
Have a flag that you are waiting for user input.
var isWaiting = false;
and then check the value of that flag in do_something (obviously set it where necessary as well :) ).
if (isWaiting) return;
You might want to implement this higher up the call stack (what calls do_something()?), but this is the approach you need.

Categories