How can i run some code at an exact timestamp? - node.js

I have a REST API in a backend with an array of objects with a timestamp (time when something happens in-game) and a value.
{"timestamp":1623320102097,"crops":[0,5,9]}
How can i do run something when time is equals to that timestamp ?

I presume that timestamps are in msec. Probably this should do the trick:
let diffMsec = obj.timestamp - new Date().getTime();
if (diffMsec > 0) {
setTimeout(function() {
/* do your stuff */
}, diffMsec);
}
Keep in mind though, that it is not guaranteed that timeout will be invoked at the exact time.

Related

I am trying to get readable date from firestore

I am trying to get readable date from Timestamp data type in my firestore database.
for (var ticketDoc of ticketsSnapshot.docs) {
var timeStamp = await ticketDoc.data().TimePreferred;
console.log(timeStamp.toDate());
var time = new Date(timeStamp).toDate();
ticketDoc.data().TimePreferred = time;
tickets.push(ticketDoc.data());
}
I read the question about a similar problem at :
How do I convert a Firestore date/Timestamp to a JS Date()?
so, i tried to do same and i expect the output of readable date, although it gives me the correct result in
console.log(timeStamp.toDate());
but also it gives me an error. Console output as follow :-
2019-04-10T06:30:00.000Z
TypeError: (intermediate value).toDate is not a function
Not : I am trying to get readable date in postman
I don't know why this timestamp object doesn't have the .toDate() extension, but if it has the 'seconds' and 'nanoseconds' properties, you can turn it to a JS Data with
Date(data.seconds)
Change the following line:
var time = new Date(timeStamp).toDate();
into this:
var time = new Date(timeStamp).toDateString();
From the docs:
A string representing the date portion of the given Date object in human readable form in American English.
You can try in the following way
{{ formatDate(new Date(data.seconds*1000)) }}
You can use the format date function to display in desired format.
import moment from "moment";
format_date(value) {
if (value) {
return moment(String(value)).format("DD/MM/YYYY");
}
},
Have you tried changing this to
var time = (new Date(timeStamp)).toDateString();
If the TimePreferred field in your document is a Timestamp, you can get a valid Date object from it by simply calling toDate() on it.
So:
for (var ticketDoc of ticketsSnapshot.docs) {
var date = ticketDoc.data().TimePreferred.toDate();
}
None of these calls are asynchronous or returning a promise, so you don't need await.
From reasons that I don't know, it doesn't work at times, so a safer option would be to use the seconds and nanoseconds attributes found in the timestamp to convert it to date as follows:
const date = new Date(timestamp.seconds*1000 + timestamp.nanoseconds/100000)
// construct the date from the absolute time in milliseconds
Note:
1 second = 1000 ms
1 nanosecond = 10^-6 ms
You have to first make sure that the timestamp object is truly of type Timestamp.
to do this, after you get the Timestamp from Firebase, create the Timestamp object:
const timestampObject: Timestamp = !!timeStamp
? new Timestamp(timeStamp.seconds, timeStamp.nanoseconds)
: null;
For Angular
import { Location, DatePipe } from '#angular/common';
constructor(
public datepipe: DatePipe
) { }
const dayAsTimestamp = your_Timestamp_value;
const dayAsDate = new Date(dayAsTimestamp.seconds * 1000);
const dayAsString = this.datepipe.transform(dayAsDate, 'dd-MMM-yyyy');

How to come around Firebase Realtime Database's server side timestamp volatility when one wants to compare timestamps on updates?

After reading the docs on ServerValue.TIMESTAMP, I was under the impression that once the object hits the database, the timestamp placeholder evaluates once and remains the same, but this was not the case for me:
// Example on Node:
> const db = f.FIREBASE_APP.database();
> const timestamp = f.FIREBASE_APP.database.ServerValue.TIMESTAMP;
> const ref = db.ref('/test');
> ref.on(
... 'child_added',
... function(snapshot) {
..... console.log(`Timestamp from listener: ${snapshot.val().timestamp}`);
..... }
... )
> var child_key = "";
> ref.push({timestamp: timestamp}).then(
... function(thenable_ref) {
..... child_key = thenable_ref.key;
..... }
... );
Timestamp from listener: 1534373384299
> ref.child(child_key).once('value').then(
... function(snapshot) {
..... console.log(`Timestamp after querying: ${snapshot.val().timestamp}`);
..... }
... );
> Timestamp after querying: 1534373384381
> 1534373384299 < 1534373384381
true
The timestamp is different when queried from the on listener and it is different during a later query.
Is this like this by design and I just missed some parts of the documentation? If this is the case, when does the ServerValue.TIMESTAMP stabilize?
I am building a CQRS/ES library on the Realtime Database, and just wanted to avoid the expected_version (or sequence numbers) of events.
UPDATE
The proof for Frank's explanation below:
/* `db`, `ref` and `timestamp` are defined above,
and the test path ("/test") has been deleted
from DB beforehand to avoid noise.
*/
> ref.on(
... 'child_added',
... function(snapshot) {
..... console.log(`Timestamp from listener: ${snapshot.val().timestamp}`);
..... }
... )
> ref.on(
... 'value',
... function(snapshot) {
..... console.log(snapshot.val());
..... }
... )
> ref.push({timestamp: timestamp}); null;
Timestamp from listener: 1534434409034
{ '-LK2Pjd8FS_L8hKqIpiE': { timestamp: 1534434409034 } }
{ '-LK2Pjd8FS_L8hKqIpiE': { timestamp: 1534434409114 } }
Bottom line is, if one needs to rely on immutable server side timestamps, keep this in mind, or work around it.
When you perform the ref.push({timestamp: timestamp}) the Firebase client immediately makes an estimate of the timestamp on the client and fires an event for that locally. It then send the command off to the server.
Once the Firebase client receives the response from the server, it checks if the actual timestamp is different from its estimate. If it is indeed different, the client fires reconciliatory events.
You can most easily see this by attaching your value listener before setting the value. You'll see it fire with both the initial estimates value, and the final value from the server.
Also see:
How to use the Firebase server timestamp to generate date created?
Trying to convert Firebase timestamp to NSDate in Swift
firebase.database.ServerValue.TIMESTAMP return an Object
CAVEAT: After wasting another day, the ultimate solution is not to use Firebase server timestamps at all, if you have to compare them in a use case that is similar to the one below. When the events come in fast enough, the second 'value' update may not trigger at all.
One solution, to the double-update condition Frank describes in his answer, is to get the final server timestamp value is (1) to embed an on('event', ...) listener inside an on('child_added', ...) and (2) remove the on('event', ...) listener as soon as the specific use case permits.
> const db = f.FIREBASE_APP.database();
> const ref = db.ref('/test');
> const timestamp = f.FIREBASE_APP.database.ServerValue.TIMESTAMP;
> ref.on(
'child_added',
function(child_snapshot) {
console.log(`Timestamp in 'child_added': ${child_snapshot.val().timestamp}`);
ref.child(child_snapshot.key).on(
'value',
function(child_value_snapshot) {
// Do a timestamp comparison here and remove `on('value',...)`
// listener here, but keep in mind:
// + it will fire TWICE when new child is added
// + but only ONCE for previously added children!
console.log(`Timestamp in embedded 'event': ${child_value_snapshot.val().timestamp}`);
}
)
}
)
// One child was already in the bank, when above code was invoked:
Timestamp in 'child_added': 1534530688785
Timestamp in embedded 'event': 1534530688785
// Adding a new event:
> ref.push({timestamp: timestamp});null;
Timestamp in 'child_added': 1534530867511
Timestamp in embedded 'event': 1534530867511
Timestamp in embedded 'event': 1534530867606
In my CQRS/ES case, events get written into the "/event_store" path, and the 'child_added' listener updates the cumulated state whenever new events come in, where each event has a ServerValue.TIMESTAMP. The listener compares the new event's and the state's timestamp whether the new event should be applied or it already has been (this mostly matters when the server has been restarted to build the internal in-memory state). Link to the full implementation, but here's a shortened outline on how single/double firing has been handled:
event_store.on(
'child_added',
function(event_snapshot) {
const event_ref = event_store.child(event_id)
event_ref.on(
'value',
function(event_value_snapshot){
const event_timestamp = event_value_snapshot.val().timestamp;
if ( event_timestamp <= state_timestamp ) {
// === 1 =======
event_ref.off();
// =============
} else {
var next_state = {};
if ( event_id === state.latest_event_id ) {
next_state["timestamp"] = event_timestamp;
Object.assign(state, next_state);
db.ref("/state").child(stream_id).update(state);
// === 2 =======
event_ref.off();
// =============
} else {
next_state = event_handler(event_snapshot, state);
next_state["latest_event_id"] = event_id;
Object.assign(state, next_state);
}
}
}
);
}
);
When the server is restarted, on('child_added', ...) goes through all events already in the "/event_store", attaching on('value',...) dynamically on all children and compares the events` timestamps to the current state's.
If the event is older than the age of the current state (event_timestamp < state_timestamp is true), the only action is detaching the 'value' listener . This callback will be fired once as the ServerValue.TIMESTAMP placeholder has already been resolved once in the past.
Otherwise the event is newer, which means that it hasn't been applied yet to the current state and ServerValue.TIMESTAMP also hasn't been evaluated yet, causing the callback to fire twice. To handle the double update, this block saves the actual child's key (i.e., event_id here) to the state (to latest_event_id) and compares it to the incoming event's key (i.e., event_id):

Make update loop in sync with music and notes in Phaser

I'm trying to make some notes appearing when the music hits a certain time in Phaser, but when I log the "hit times" in the console, it only show up sometimes.
I have an object of "notes", the key being the time I expect the note to show :
{
1377: {
jam: 1,
duration: 0.40
}
}
with 1464 notes.
But, in the update loop, if I do something like this :
update () {
if (music && music.currentTime) {
if (notes[music.currentTime]) {
console.log('notes[music.currentTime].jam', notes[music.currentTime].jam)
}
}
}
It logs only some of the notes, randomly.
Do you have any idea why ?
This is probably because music.currentTime is incrementing by ~16 ms on each update, so it can skip specific time keys from your notes object. Other than that I believe that the time can also be a floating point value, so it won't exactly match your keys in the notes variable.
An alternate way to implement what you want would be to change format of the notes variable to an array so it could be accessed later in a different manner:
var notes = [
...
{'start': 1377, 'jam': 1, 'duration': 0.40},
{'start': 2456, 'jam': 1, 'duration': 0.30},
...
];
// Index of the first note that will be played.
// Will be incremented by 1 or more with some update() calls,
// depending on the time that passed.
var nextNote = 0;
function update() {
// Process all notes that are now in the past
// compared to the current time of the playing music,
// but skip notes that have been already played before.
while (music && nextNote < notes.length && notes[nextNote].start <= music.currentTime) {
console.log(notes[nextNote]);
nextNote += 1;
}
}
For this method to work, the notes array must hold start times in an ascending order.

synchronous execution of database function in node.js

There are two collections products and stock in my database.Each product have multipple stock with different supplier and attributes of products.
I have selected the products from products collection and run a loop for each product. I need to append price and offer price from the stock collection to products i have selected already.
I think the for loop compleated its execution before executiong the find method on stock collection.I need to execute everything in the loop in serial manner(not asynchronous). Please check the code below and help me to solve this. I'm new in node.js and mongodb
collection = db.collection('products');
collection.find().toArray(function(err, abc) {
var finalout = [];
for( var listProducts in abc){
var subfinal = {
'_id' :abc[listProducts]['_id'],
'min_price' :abc[listProducts]['value']['price'],
'stock' :abc[listProducts]['value']['stock'],
'name' :abc[listProducts]['value']['name'],
'price' :'',
'offer_price' :'',
};
collection = db.collection('stock');
collection.find({"product":abc[listProducts]['_id'] ,"supplier":abc[listProducts]['value']['def_supplier']}).toArray(function(err, newprod) {
for( var row in newprod){
subfinal['price'] = newprod[row]['price'];
subfinal.offer_price = newprod[row]['offer_price'];
}
finalout.push(subfinal);
});
}
console.log(finalout);
});
Yes, the loop is running and starting the get requests on the database all at the same time. It is possible, like you said, to run them all sequentially, however it's probably not what you're looking for. Doing it this way will take more time, since each request to Mongo would need to wait for the previous one to finish.
Considering that each iteration of the loop doesn't depend on the previous ones, all you're really looking for is a way to know once ALL of the operations are finished. Keep in mind that these can end in any order, not necessarily the order in which they were initiated in the loop.
There's was also an issue in the creation of the subfinal variable. Since the same variable name is being used for all iterations, when they all come back they'll be using the final assignment of the variable (all results will be written on the same subfinal, which will have been pushed multiple times into the result). To fix that, I've wrapped the entire item processing iteration into another function to give each subfinal variable it's own scope.
There are plenty of modules to ease this kind of management, however here is one way to run some code only after all the Mongo calls are finished using a counter and callback that doesn't require any additional installs:
var finished = 0; // incremented every time an iteration is done
var total = Object.keys(abc).length; // number of keys in the hash table
collection = db.collection('products');
collection.find().toArray(function(err, abc) {
var finalout = [];
for( var listProducts in abc){
processItem(listProducts);
}
});
function processItem(listProducts) {
var subfinal = {
'_id' :abc[listProducts]['_id'],
'min_price' :abc[listProducts]['value']['price'],
'stock' :abc[listProducts]['value']['stock'],
'name' :abc[listProducts]['value']['name'],
'price' :'',
'offer_price' :'',
};
collection = db.collection('stock');
collection.find({"product":abc[listProducts]['_id'] ,"supplier":abc[listProducts]['value']['def_supplier']}).toArray(function(err, newprod) {
for( var row in newprod){
subfinal['price'] = newprod[row]['price'];
subfinal.offer_price = newprod[row]['offer_price'];
}
finalout.push(subfinal);
finished++;
if (finished === total) { // if this is the last one to finish.
allFinished(finalout);
}
});
}
function allFinished(finalout) {
console.log(finalout);
}
It could be written more concisely with less variables, but it should be easier to understand this way.

How to specify different delays between slides in bxslider

Ran across the following problem in bxslider- how can you apply different delays between slides in the auto show?
I came up with the following solution which I will show here:
in the jquery.bxslider.js replace:
el.startAuto = function(preventControlUpdate){
// if an interval already exists, disregard call
if(slider.interval) return;
// create an interval
slider.interval = setInterval(function(){
slider.settings.autoDirection == 'next' ? el.goToNextSlide() : el.goToPrevSlide();
}, slider.settings.pause);
// if auto controls are displayed and preventControlUpdate is not true
if (slider.settings.autoControls && preventControlUpdate != true) updateAutoControls('stop');
}
With
/**EDITS: By CRB - techdude **/
el.startAuto = function(preventControlUpdate){
el.continueAuto();
}
el.continueAuto = function(){
//get how long the current slide should stay
var duration = slider.children.eq(parseInt(slider.active.index)).attr("duration");
if(duration == ""){
duration = slider.settings.pause;
} else {
duration = parseInt(duration);
}
console.log(duration);
// create a timeout
slider.timer = setTimeout(function(){
slider.settings.autoDirection == 'next' ? el.goToNextSlide() : el.goToPrevSlide();
el.continueAuto();
}, duration);
// if auto controls are displayed and preventControlUpdate is not true
if (slider.settings.autoControls && preventControlUpdate != true) updateAutoControls('stop');
}
//*End Edits*/
Then to change the duration of a slide, simply give its li tag a duration attribute like this:
where duration is the number of milliseconds for the slide to pause.
To set the default duration, simply use the pause: option in the settings:
$("element").bxSlider({
auto:true,
pause: 4000
};
Hope this helps. Maybe bx slider will even add it to a future version. :)
What are the you're using to pick this up? Any way you can put up a gist of it working?
Perhaps this will help clarify:
In principle, the way this works is I change the setInterval with a setTimeout so the interval can be changed each time.
The key to getting multiple elements to work on a page is to not use the slider.timer object, but probably to use the el.timer object so the line would read something like,
el.timer = setTimeout(function(){
slider.settings.autoDirection == 'next' ? el.goToNextSlide() : el.goToPrevSlide();
el.continueAuto();
}, duration);
Instead of
slider.timer = setTimeout(function(){
slider.settings.autoDirection == 'next' ? el.goToNextSlide() : el.goToPrevSlide();
el.continueAuto();
}, duration);
I haven't tested it with multiple sliders, but let me know if this works. That is the principle anyway. The only problem with this, however, is that I believe that you would need to modify the el.pause method to use the el.timer as well, otherwise the slideshow can't be paused. I think that was the reason I did it the way I did. However, it was a long time ago.

Resources