Data retrival and assignment in Reactjs - javascript

I am running Reactjs and mongdb.
In Reactjs, once user selects the value, I am sending that into mongob
addTodo(event) {
event.preventDefault();
var roomID = this.state.selectedRoomID;
console.log(RoomID); //This value is printed correctly
const { mongodb } = this.props;
mongodb.db("attendance")
.collection("MasterDB").find({"RoomID": roomID}).asArray()
.then(MasterDB => {
this.setState({ MasterDB }); console.log("inside"+this.state.MasterDB.length);
});
console.log("outside"+this.state.MasterDB.length)
a) Here first console.log is printing correctly.
b) when ever I do find with roomID=1 explicitly the Query method is working.
whenever I keep roomID=this.state.selectedRoomID;.
Query is not working because roomID is not getting any value.
I am not sure why it is not getting that value.
c) When I execute I see,
console.log(outside) is printed first and than
console.logg(inside) is printed.
As this is sequential, I am expecting the order other way.
is there a way I can make
sure first the lookup in mongodb happens first that is (console.log(inside) is printed first followed by
console.log(outside)?
I see this as issue with reactjs the way I am sending the variables

I need to look at the code when selectedRoomID is set to figure out why it is not getting any value.
As for the code to execute synchronously, either use Promises or Async/Await. That should help you run the code in the way you want it.
For example, in the code you posted, if you put the console log statement inside the then function, it should work fine. Or you can make the whole mongodb command a promise and wait for it to finish before proceeding further.

Related

How to delay function call until its callback will be finished in other places

I'm using child_process to write commands to console, and then subscribe on 'data' event to get output from it. The problem is that sometime outputs are merged with each other.
let command = spawn('vlc', { shell: true });
writeCommand(cmd, callback) {
process.stdin.write(`${cmd}\n`);
this.isBusy = true;
this.process.stdout.on('data', (d) => {
callback(d);
});
}
Function writeCommand is used in several places, how can I delay it from executing until output from previous command is finished?
My output can look like (for status command for example):
( audio volume: 230 ) ( state stopped ) >
data events on a stream have zero guarantees that a whole "unit" of output will come together in a single data event. It could easily be broken up into multiple data events. So, this combined with the fact that you are providing multiple inputs which generate multiple outputs means that you need a way to parse both when you have a complete set of output and thus should call the callback with it and also how to delineate the boundaries between sets of output.
You don't show us what your output looks like so we can't offer any concrete suggestions on how to parse it in that way, but common delimiters are double line feeds of things like that. It would entirely depend upon what your output naturally does at the end or if you control the content the child process creates, what you can insert at the end of the output.
Another work-around for the merged output would be to not send the 2nd command until the 1st one is done (perhaps by using some sort of pending queue). But, you will still need a way to parse the output to know when you actually have the completion of the previous output.
Another problem:
In the code you show, every time you call writeCommand(), you will add yet another listener for the data event. So, when you call it twice to send different commands, you will now have two listeners both listening for the same data and you will be processing the same response twice instead of just once.
let command = spawn('vlc', { shell: true });
writeCommand(cmd, callback) {
process.stdin.write(`${cmd}\n`);
this.isBusy = true;
// every time writeCommand is called, it adds yet another listener
this.process.stdout.on('data', (d) => {
callback(d);
});
}
If you really intend to call this multiple times and multiple commands could be "in flight" at the same time, then you really can't use this coding structure. You will probably need one permanent listener for the data event that is outside this function because you don't want to have more than one listener at the same time and since you've already found that the data from two commands can be merged, even if you separate them, you can't use this structure to capture the data appropriately for the second part of the merged output.
You can use a queuing mechanism to execute the next command after the first one is finished. You can also use a library like https://www.npmjs.com/package/p-limit to do it for you.

Is there a way to get the actual timestamp when using firebase.database.ServerValue.TIMESTAMP?

This question is about the javascript client.
I have code that goes something like this:
const localEvents = [];
const fbEvents = firebase.database().ref("myevents");
fbEvents.on("child_added", function(snapshot) {
const e = snapshot.val();
localEvents.push(e);
});
function createEvent(e) {
e.time = firebase.database.ServerValue.TIMESTAMP;
fbEvents.push(e);
}
After calling createEvent({}), it appears that entries in my localEvents list have time values which are not equal to the actual entries in the database (the client guesses the timestamp and calls the child_added handler before it's actually done a roundtrip to the server). Is there any way to avoid this, and/or is there any way to get a callback when the actual value of the time is known?
It's not possible, using only the snapshot in the listener, to determine if the timestamp comes from the server or is guessed locally.
What you can do instead is use the promise returned from fbEvents.push(e) to determine when the write actually succeeds. A resolved promise which means it was definitely written to Firebase. The listener callback you get after that will contain the server's updated value.
(Note that with Firestore it is possible to determine if a document was fully written to the server or not. Just not with Realtime Database.)

Using data returned from Firebase fetch Javascript

Web dev novice here. I'm using Firebase as my backend, and I have a situation where I need to query the DB for one value and use that returned value to match in another query to the same DB.
Here is my code:
function dataLoad() {
var valueThatINeedToUseElsewhere
userReference.on('value',function(users){
var currentUser = firebase.auth().currentUser.uid
users.forEach(function(user){
if(user.key === currentUser) {
valueThatINeedToUseElsewhere = user.val().userName
console.log(valueThatINeedToUseElsewhere)
}
})
})
console.log(valueThatINeedToUseElsewhere)
}
The console.log inside the IF condition logs the correct value. However, the last console.log above does not log anything, despite the variable name being declared outside the 'on' function. Why is this happening? And how can I actually use the data outside the 'on' function? I need to use it to perform a similar IF condition in another data retrieval operation.
Thanks!
You're likely trying to do something like this now:
var valueThatINeedToUseElsewhere;
dataLoad();
doSomethingWith(valueThatINeedToUseElsewhere);
Unfortunately this doesn't work, since (as Doug says) the data is loaded from Firebase asynchronously. By the time you're trying to use valueThatINeedToUseElsewhere, the value hasn't been loaded yet.
Why this doesn't work, the one-three-two test
It's easiest to see this if you place a few log statements like this:
console.log("Before starting to load value")
userReference.on('value',function(users){
console.log("Loaded value")
})
console.log("After starting to load value")
When you run this code, it prints:
Before starting to load value
After starting to load value
Loaded value
That is probably not the order that you expected. But it perfectly explains why you can't use valueThatINeedToUseElsewhere when you try: it hasn't been loaded yet.
Reframing the problem
The best way I've found for dealing with this situation is to reframe the problem. Your current code is written with the logic of "first we load the value, then we do something with the value". For asynchronous loading it's better to frame it as "first start loading the data. once the data has started loading, do something with it".
In code that look like:
function dataLoad() {
userReference.once('value',function(users){
var currentUser = firebase.auth().currentUser.uid
users.forEach(function(user){
if(user.key === currentUser) {
var valueThatINeedToUseElsewhere = user.val().userName
doSomethingWith(valueThatINeedToUseElsewhere)
}
})
})
}
As you'll see, we've moved the code that needs the data into the callback that fires when the data is available. That way you can be guaranteed the data is available when you call doSomethingWith. I also changed the code to use once, which (as Doug said) is better for your use-case.
There are two more steps to take, both to improve the flexibility and performance of your code.
Passing in a function that is called once the data is loaded
First off: with this last update, the call to doSomethingWith is hard-coded, which means that dataLoad() and doSomethingWith are closely tied together. It's often better to keep them more separate. To do that, we can pass in a so-called callback function into dataLoad that it then calls when it has loaded the data. This is very similar to what once() does already. Let's see that in practice:
function dataLoad(callback) {
userReference.once('value',function(users){
var currentUser = firebase.auth().currentUser.uid
users.forEach(function(user){
if(user.key === currentUser) {
var valueThatINeedToUseElsewhere = user.val().userName
callback(valueThatINeedToUseElsewhere)
}
})
})
}
Not too different. But now you can invoke dataLoad like this:
dataLoad(doSomethingWith);
And with that doSomethingWith will be called with the data, once the data has been loaded.
Loading the data by its key
The final change is an optimization. Your current code is loading all users, while you only need one and know their key. It's much more efficient to only load that specific user, with:
function dataLoad(callback) {
var currentUser = firebase.auth().currentUser.uid
userReference.child(currentUser).once('value',function(users){
var valueThatINeedToUseElsewhere = user.val().userName
callback(valueThatINeedToUseElsewhere)
})
}
on() is asychronous and returns immediately, which means your console log is going to show an undefined value. The callback you pass to on() is only going to run when results are available, and there's no guarantee how quickly that will happen. You should only use a value from an asynchronous call after it finishes - don't try to make your code block until some async call is finished.
Also consider using once() instead of on() if you only need the value at a location a single time. You can use its returned promise to chain some additional work after it loads the data you want. It is also asynchronous (as is all other methods that return a promise).
To learn more about why Firebase APIs are asynchronous, and what that means for your app, read this blog.

Cloud Function stuck in an infinite loop

exports.addNewValue = functions.database.ref('/path')
.onWrite(event => {
event.data.adminRef.update({"timeSnapshot":Date.now()})})
It appears that Date.now() causes an infinite loop in the function because the following does not:
exports.addNewValue = functions.database.ref('/path')
.onWrite(event => {
event.data.adminRef.update({"timeSnapshot":"newString"})})
How do I fix this?
If you write back to the same location in the database that was previously changed, you can expect this sequence of events:
Function is triggered with the first change from the client
Function writes back to the database
Function is triggered a second time because of the write during step #2
All writes to the database that match the filter path, even those from within the same function, will trigger the function.
In step 3, you need a strategy to figure out if the second function invocation should result in yet another write back to the database. If it does not require another write, the function should return early so that it won't trigger another write. Typically you look at the data in the event passed to the function and figure out if it was already modified the first time. This could involve looking to see if some flag is set in the database, or if the data you modified does not need any more changes.
Many of the code samples provided by the Firebase team do this. In particular, look at text moderation. Also, there is a video that describes the problem and a possible solution. In the end, you're responsible for coming up with the strategy that meets your needs.
I think the following should work fine :-
exports.addNewValue = functions.database.ref('/path/timeSnapshot')
.onWrite(event => { event.data.adminRef.set(Date.now()) })
The logic behind the above is that when you put a trigger function on a higher node (such as /path in your case), then the function would be fired each time a change is made to any of its child nodes (/timestamp in your case) - hence, the infinite loop.
Therefore, as a general practice, for efficiency as well as cost effectiveness, make sure that your trigger function has the lowest possible path node. Flatting out your data really helps in this as well.
If you arrived here having problems with querying try using .once('value') ... it will mean that you only look at the reference point once ... i.e.
ref.orderByChild("isLive").equalTo(true).once("value" , function(snapshot) {
instead of
ref.orderByChild("isLive").equalTo(true).on("value", function(snapshot) {
as the second will have you listening all the time, and when data changes at the ref, the listener will receive the changes and run the code inside your block again

How to make this function execute in time for the next code?

How to make this garbage variable get read as the value of shipmentObjs in time? I know the console.log will read "undefined" but I want it to execute after the function. How? I tried several ways and I can't seem to figure out a way to make this happen
let garbage;
db.scan({TableName: 'shipments'}, (err,shipments) => {
if (err) console.error(err);
// We got our shipments
let shipmentObjs = shipments.Items;
// Bring out the "done: false" shipments to display
shipmentObjs = shipmentObjs.filter(obj => obj.done === false);
garbage = shipmentObjs;
});
});
// prints "undefined" . Must figure out a way to make it read shipmentObjs from the above code
console.log(garbage);
console.log(garbage);
to work the above code fine you need to put it in he db.scan function.
Two things. First of all, the order doesn't matter, if you're calling async function - which db.scan() is. As #Amresh said, use promise or callbacks for this. Second problem with your code is what's happening inside the db.scan() func. I don't know what db library you're using, but most likely you won't receive any schema, if you'll get an error. Therefore, your code will crash on your shipmentObjs.filter try. You need to take care of this part. Simply return your error.

Categories