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.
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.
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.
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.
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 .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.