I am working on a firebase project. During testing the
return user.val().name;
will return an
undefined
value however the
console.log(user.val().name)
will return the actual string stored in the .name field. Why is that. Also even if assign the
user.val().name
to a variable, the variable remains undefined.Please help figure out why this happens. I am printing it to a csv.
Here is my code:
var database = firebase.database();
var ref2 = database.ref('information/');
var id;
var name;
ref2.on("value", function (one) {
one.forEach(function (two) {
if (typeof two.val().Id !== 'undefined') {
id = two.val().Id;
name = program(id); //name undefined
}
else {
id = "";
}
csv = name + "," + id +"\n";
});
download(csv);
});
};
function program (id) {
var database = firebase.database();
var ref = database.ref("users/" + id + "/");
ref.on('value',function(user){
if (typeof user.val().name === 'undefined') {
return null;
}
else {
console.log(user.val().name); //this doesnt show undefined
return user.val().name; //this shows undefined when appended to a html element
}
})
}
Note: In the firebase database, the name value is not null. It has a string added to it.
I second with Frank's reason on why your function program() doesn't work. Because ref.on('value'... makes an asynchronous call, program() does not wait for the completion of ref.on and exists with an undefined return value.
What you could instead do is use Promises. Wrap the statements inside your program() function within a Promise, and upon completion of the asynchronous call, resolve or reject based on the result it gives.
Here's your function with Promises:
function program(id) {
return new Promise(function (resolve, reject) {
try {
var database = firebase.database();
var ref = database.ref("users/" + id + "/");
ref.on('value', function (user) {
if (typeof user.val().name === 'undefined') {
resolve(null);
} else {
console.log(user.val().name);
resolve(user.val().name);
}
})
} catch (e) {
reject(e)
}
});
}
And then, here's how you can read the result:
program(id).then(function (result) {
console.log(result)
//Do what you want with the result here
}).catch(function (error) {
console.log(error)
})
Note: You're executing this block in a for-each statement. If you're using Promises, you'd also need to look into how to use Promises inside a loop. For reference, check Promise.all()
Most likely you are trying to use the returned name in the code that calls your program function. E.g.
var name = program("1234");
console.log(name); // this will print undefined
This will not work, since your program() is not actually returning name. Data is loaded from Firebase asynchronously. By the time program() exits, the data isn't loaded yet.
This is easiest to see by putting a few log statements into the code:
function program (id) {
var database = firebase.database();
var ref = database.ref("users/" + id + "/");
console.log("Before attaching listener");
ref.on('value',function(user){
console.log("Got value from database");
})
console.log("After attaching listener, exiting function");
}
This will print:
Before attaching listener
After attaching listener, exiting function
Got value from database
This is likely not the order that you expected, but it is working as designed. Instead of waiting for the data to be loaded (and making the browser/user wait), your code continues. Then when the data is loaded, your callback is invoked. But in your original code that means that your return statement is unable to return the name to the original caller.
This is precisely the reason why the Firebase Database (and most web APIs) use callbacks like the one you pass into on(). This callback is invoked when the data is available and is the only place where you can access the user data. So any code that requires the data you just loaded must be inside that callback, or be called from inside that callback. E.g.
function program (id) {
var database = firebase.database();
var ref = database.ref("users/" + id + "/");
ref.on('value',function(user){
if (typeof user.val().name === 'undefined') {
return null;
}
else {
console.log(user.val().name);
appendToHtmlElement(user.val().name);
}
})
}
Firebase> DataBase> Role
Have you changed the value of rules?
Comment Example!
That can get the value after moving to name.
var ref = database.ref ("users/" + id + "/name");
ref.on ('value', function (user) {
if (user.val ()! = null) {
console.log (user.val ())
}
}
If not, let's worry about it.
You should receive the returned value.
var name = ref.on('value',function(user){
if (typeof user.val().name === 'undefined') {
return null;
}
else {
console.log(user.val().name); //this doesnt show undefined
return user.val().name; //this shows undefined when appended to a html element
}
})
then use return name to get the value. or simply return the return ref.on('value' ...
Related
I'm trying to check if a user exists (registered on a json file).
Unfortunately I don't find a valid solution in all Stack Overflow that gives me a simple "true" in a callback.
The version closest to a solution
Experiment V1 :
let userExist;
function check(){
console.log("CHECK!");
return userExist = true;
}
// check(); if this, return true... obvious.
//// check if user exist
server.readFileSync(filepath, 'utf8', (err, data) =>
{
let json = JSON.parse(data),
userlist = json.allusers;
for (let key in userlist)
{
if ( userlist[key].email == req.body.user_email )
{
console.log("FINDED EQUAL");
check(); // return undefined ???
}
}
});
console.log("userExist value : "+userExist);
differently formulated the debugs also appear, but "true" never returns.
note: yes, JSON is read correctly. If everything works inside the readfile, you immediately notice the same emails.
output: "undefined"
Log: total bypassed
Experiment V2 :
In this case (with asynchronous reading) it returns all the debugging (but the "true" remains undefined)
The problem with the asynchronous is that I have to wait for it to check to continue with the other functions.
//// check if user exist
server.readFile(filepath, 'utf8', (err, data) =>
{
let json = JSON.parse(data),
userlist = json.allusers;
for (let key in userlist)
{
if (/* json.allusers.hasOwnProperty(key) &&*/ userlist[key].email == req.body.user_email )
{
console.log("FINDED EQUAL");
check();
}
}
});
var userExist;
function check(userExist){
console.log("CHECK!");
return userExist=true;
}
console.log("userExist value : "+userExist+"");
server listening on: 8080
userExist value : undefined
CHECK!
FINDED EQUAL
Experiment V3 :
after the various suggestions I come to a compromise by using the syntax for the async functions.
This allowed to reach an ordered code, but despite this it is not possible to wait for the results and export them out of the same function (this is because node itself is asynchronous! Therefore it has already gone on!)
using a "message" variable to check if it could return an object I did so:
//simple output tester
var message;
// create a promise
let loopusers = new Promise( (resolve)=>{
server.readFile( filepath, 'utf8',
(err, data) => {
let json = JSON.parse(data),
userlist = json.allusers,
findedequal;
console.log("CHECK USERS IN DB...for "+userlist.length+" elements");
// loop all items
for (let key in userlist)
{
console.log("Analyzed key ::: "+key);
if ( userlist[key].email == req.body.user_email )
{
console.log("CHECK : user isn't free");
findedequal=true;
resolve(true); // return the result of promise
}
else if(key >= userlist.length-1 && !findedequal )
{
console.log("CHECK : User is free ;)");
resolve(false); // return the result of promise
}
}
// call the action
createuser();
});
});
// when promise finished --> start action
async function createuser(message)
{
let userExist = await loopusers;
console.log("userExist: "+userExist);
if(userExist)
{
message = { Server: "This user already exists, Try new e-mail..."};
}
else
{
message = { Server: "Registration user -> completed..."};
}
// return values
return message;
};
It is also possible to use the classic syntax via "then". For exemple:
//simple output tester
var message;
// create a promise
let loopusers = new Promise( (resolve)=>{
...
});
loopusers.then( (response)=>{
...
})
Then I realized that it was easy to simplify even more by calling the functions directly from the initial one:
var message;
// create a promise --> check json items
server.readFile( filepath, 'utf8',
(err, data) => {
let json = JSON.parse(data),
userlist = json.allusers,
findedequal;
console.log("CHECK USERS IN DB...for "+userlist.length+" elements");
for (let key in userlist)
{
console.log("Analyzed key ::: "+key);
if ( userlist[key].email == req.body.user_email )
{
console.log("CHECK : user isn't free");
findedequal=true;
createuser(true); // call direct function whit params true
}
else if(key >= userlist.length-1 && !findedequal )
{
console.log("CHECK : User is free ;)");
createuser(false); // call direct function whit params false
}
}
});
// start action
function createuser(userExist)
{
if(userExist)
{
message = { Server: "This user already exists, Try new e-mail..."};
}
else
{
message = { Server: "Registration user -> completed!"};
}
// return values
return message;
};
debugging is taken and written
the message is lost outside the aSync function
Experiment V4 Final! :
Finally, after many attempts the solution! (Yes... But know it's not Async)
If we allocate in a variable the reading becomes synchronous the whole model and we return to the simple one
let message,
file = server.readFileSync(filepath, 'utf8'), // read sync
json = JSON.parse(file), // now parse file
userlist = json.allusers, // get your target container object
userExist,
findedequal;
console.log("CHECK USERS IN DB...for "+userlist.length+" elements");
for (let key in userlist)
{
console.log("Analyzed key ::: "+key);
if ( userlist[key].email == req.body.user_email )
{
console.log("CHECK : finded equal value on key ["+key+"] - user isn't free");
findedequal=true;
userExist = true;
}
else if(key >= userlist.length-1 && !findedequal )
{
console.log("CHECK : User is free ;)");
userExist = false;
}
}
if(userExist)
{
console.log("└ EXIT TO CHECK --> Can't create user, function stop.");
message = { Server: "This user already exists, Try new e-mail..."};
}
else
{
console.log("└ Exit to check --> New user registration ...");
message = { Server: "Registration user -> completed!"};
}
}
return message;
Now:
It's all sync and all log is perfect
all var is checked
all return... return
** Final conclusions: **
Is it possible to retrieve an ASync variable in node?
As far as I understand so far ... no.
Node is async by its nature, therefore recovering information that is not saved and then recovered from a DB is left behind among the things to do, becoming unrecoverable if you use it as in this post.
However ... if the purpose is to make reading a file synchronous, the answer was simpler than expected.
A special thanks to: Barmar; Austin Leehealey; C.Gochev;
The problem is that you are calling console.log("userExist value : "+userExist+"");
too early. At the moment that you call that line, userExist is not defined yet. This is because the server.readFile() function requires a callback and that callback function is executed once it has read the file. However, reading files often take time and so the program keeps going. It executes console.log("userExist value : "+userExist+""); and then goes back to the callback function and defines userExist as true.
If you want more information on what callbacks are look at the link below. Callbacks are a defining feature of Nodejs and understanding them is essential to any Node website.
https://medium.com/better-programming/callbacks-in-node-js-how-why-when-ac293f0403ca
Try something like this.
let userExist;
function check(){
console.log("CHECK!");
return userExist = true;
}
// check(); if this, return true... obvious.
//// check if user exist
server.readFileSync(filepath, 'utf8', (err, data) =>
{
let json = JSON.parse(data),
userlist = json.allusers;
for (let key in userlist)
{
if ( userlist[key].email == req.body.user_email )
{
console.log("FINDED EQUAL");
check(); // return undefined ???
console.log("userExist value : "+userExist);
}
}
});
I using Express with Node to interact with a MySQL database.
The create method of my Card object will save the data to the database and should update the object's id to be the insertId returned from the query.
class Card {
constructor(d) {
this.id = d.id || 0;
this.playerId = d.playerId;
this.drawn = false;
this.title = d.title
this.qty = d.qty;
this.hint = d.hint;
this.type = d.type;
this.descr = d.descr;
this.pair = d.pair
this.data = d;
}
create(conn) {
return new Promise( (resolve, reject) => {
var query = "INSERT INTO cards SET ? ";
conn.query(query, this.data, function(err, result) {
if (err) throw err;
(typeof result.insertId === 'number')
? resolve(result.insertId)
: reject("Failed to insert a new Card.");
resolve(result.insertId);
})
}).then( (id) => {
this.id = id;
console.log("Your insert id is");
console.log(this.id) // GREAT! It prints the correct id
return this; // Set the value of the promise to this Card instance
})
}
}
After creating a card, it should print the json representation of the card.
router.post('/create-card', function(req, res) {
var data = req.body;
var card = new Card(data);
card = card.create(db.connection);
res.json(card);
})
But the id of the returned object is always the default value (0).
I have played with a few different ways of trying to capture the id, set the object's id property with the new value, and persist that value to the object. However, it always seems to be a more locally scoped version of the card that gets updated, not the original card (which is printed to the page).
When using Promises, what is the proper place to assign object data and return that instance of the object?
You're calling res.json too soon. You need to await the promise's resolution:
card.create(db.connection).then( _ => res.json(card));
You are doing an async operation in create method but dont use it properly. You have to do one of two things:
Return promise from create, then wait for it to fulfilled with 'then' in your post route.
Use await and wait for the promise to be resolved.
In your code for now you are running in your router synchronize code while your create operation is async...
I'm new to chaining JavaScript promises. I read all of the answers regarding above error. Added lots of return, but still, do not understand why it is returning undefined.
I have 3 getJson calls (user, logo and stream). Data from all three are colected in thisNameInfo array and used to build html.
In one of the prevous versions all then statments were chained in one signle line. That did not produce error, but the html was build before the getJson call was executed. After reading this thread how to chain then functions I added 3 call routines (callUser, callLogo and callStream).
It passes first callUser and gives me Cannot read property 'then' of undefined for 'then' after the callLogo. Point of error is underline in the code with ``````````````.
Thanks for help.
If you have suggestiong how to better pass data from getJson calls to function that build html I would love to hear it.
Here is my code:
var allStreamers = ["freecodecamp", "animeexpo", "brunofin"];
// build html for one stereamer
var buildStreamerHtml = function(thisNameInfo){
//build html using thisNameInfo
... some code goes here
$("#display").append(html);
};
// get user
var user = function(name, thisNameInfo){
// return promise or "then" will return undefined!!!
return $.getJSON(
"https://wind-bow.glitch.me/twitch-api/users/" + name,
function(data) {
// if user does not exist data.error if 404 and data.message exist
if (data.message) {
thisNameInfo.userExist = "no";
thisNameInfo.title = data.message;
thisNameInfo.url = "#";
thisNameInfo.logo = "";
} else{
thisNameInfo.userExist = "yes";
}
});
};
// get logo, title and url
var logo = function(name, thisNameInfo){
if (thisNameInfo.userExist === "yes"){
// get logo and title with link to url
// return promise or "then" will return undefined!!!
return $.getJSON("https://wind-bow.glitch.me/twitch-api/channels/" + name,
function(dataChannel) {
thisNameInfo.url = dataChannel.url;
thisNameInfo.title = dataChannel.display_name;
thisNameLogo.logo = dataChannel.logo;
});
}
};
// get stream title and number of watchers
var stream = function(name, thisNameInfo){
if (thisNameInfo.userExist === "yes"){
// return promise or "then" will return undefined!!!
return $.getJSON("https://wind-bow.glitch.me/twitch-api/streams/" + name,
function(dataStreams) {
if (dataStreams.stream) {
thisNameLogo.status = "Online";
thisNameLogo.streamTitle = dataStreams.stream.channel.status;
thisNameLogo.viewers = dataStreams.stream.viewers;
} else {
thisNameLogo.status = "Offline";
}
});
}
};
var callUser = function(name, thisNameInfo){
return user(name, thisNameInfo).then(callLogo(name, thisNameInfo));
};
var callLogo = function(name, thisNameInfo){
return logo(name, thisNameInfo).then(callStream(name, thisNameInfo));
}; ``````````````````````````````````````
var callStream = function(name, thisNameInfo){
return stream(name, thisNameInfo);
};
// link together all asinhronious calls for one streamer
var getStreamerInfo = function(name){
"use strict";
// this variable builds up by assinhronious calls
// then its value is usedd by buildStreamerHtml
console.log("getStreamerInfo name: " + name);
var thisNameInfo = {};
callUser(name, thisNameInfo).then(buildStreamerHtml(thisNameInfo));
};
// loop through all streamers and display them
allStreamers.forEach(getStreamerInfo);
The undefine points after the second promise callLogo
It looks like your issue could be that you are not passing callback functions to each then().
When you pass callLogo(name, thisNameInfo) to then(), you are actually calling the function immediately and passing it's return value:
return user(name, thisNameInfo).then(callLogo(name, thisNameInfo));
Instead, you need to pass a callback function that will be called once the Promise resolves:
return user(name, thisNameInfo).then(function() {
callLogo(name, thisNameInfo)
});
You need to do this anytime you are using then().
I'm trying to write a Parse.com Cloud Code function to accomplish the following workflow:
User submits a value.
Cloud code function checks to see if that value matches any objects of type code.
If not, the function returns a "not found" value.
If so, the object of type code is assumed to have a pointer to an object of type item.
Then, code.item is checked to see whether it has a pointer to an object of type alert.
If not, the function returns a "not found" value.
If code.item.alert does exist, then I want to fetch the full alert object, including pointers which may or may not exist, up to 2 layers deep.
As I begin writing the code for this function, I can get it working to the point of checking to see whether the code exists and, if so, whether code.item.alert also exists.
This is where the problem arises. As it currently stands, in the working version of my function, the alert item that is returned is only the class type and objectId. I understand why that is happening, and I am trying to write code to populate the object before returning it, but I am failing in that attempt.
Here's the code that is working so far (but only returning the alert object's shell):
Parse.Cloud.define("alertLookup", function (request, response) {
Parse.Cloud.useMasterKey();
var codeQuery = new Parse.Query("code");
codeQuery.equalTo("value", request.params.code);
codeQuery.include("item");
codeQuery.find().then(function (codes) {
if (codes.length === 0) {
response.success("no item");
} else {
var code = codes[0];
var item = code.get("item");
var alert = item.get("alert");
if (alert === null || alert === undefined) {
response.success("no item");
} else {
response.success(alert);
}
}
}, function (error) {
response.error(error);
});
});
Here's what I have tried that is failing with an error code of 141:
Parse.Cloud.define("alertLookup", function (request, response) {
Parse.Cloud.useMasterKey();
var codeQuery = new Parse.Query("code");
codeQuery.equalTo("value", request.params.code);
codeQuery.include("item");
codeQuery.find().then(function (codes) {
if (codes.length === 0) {
response.success("no item");
} else {
var code = codes[0];
var item = code.get("item");
var alert = item.get("alert");
if (alert === null || alert === undefined) {
response.success("no item");
} else {
return alert.fetch();
}
}
}).then(function (a) {
response.success(a);
}, function (error) {
response.error(error);
});
});
Why won't the fetch() call work properly? When I insert console.log() statements, although alert is non-null, return alert.fetch(); does not ever seem to be called. At least, the response.success(a); line is never called. Why not?
Try this instead while chaining Promises:
codeQuery.find().then(function (codes) {
if (codes.length != 0) {
var code = codes[0];
var item = code.get("item");
var alert = item.get("alert");
if (alert != null && alert != undefined) {
var alertObj = new Parse.Object("alert"); // alert class ???
alertObj.id = alert.id;
return alertObj.fetch();
}
}
// return a Promise for no items
return Parse.Promise.as("no item");
}).then(function (a) {
response.success(a);
}, function (error) {
response.error(error);
});
I have the code snippet bellow which actually is a function that makes a query on a SQL database(used tedious for that).
All I want is to get the data from DB and use them on a web-page.
The issue is that: inside the request.on()... I'm calling date.setMyData() function. Still inside the request.on('row', function(columns)....if calling console.log(date.getMyData()) it successfully returns my data.
BUT, if trying to call date.getMyData outside the request.on('row', function(columns) ...I get NULL...
Is there anything that I'm missing here?
var date = new DBData();
function executeStatement() {
request = new Request(queryString, function(err, rowCount) {
if (err) {
console.log(err);
} else {
console.log(rowCount + ' rows');
}
connection.close();
});
request.on('row', function(columns) {
columns.forEach(function(column) {
if (column.value === null) {
console.log('NULL');
} else {
date.setMyData(columns);
//WHY THIS CODE RETURNS MY DATA?
console.log(date.getMyData());
}
});
});
request.on('done', function(rowCount, more) {
console.log(rowCount + ' rows returned');
});
connection.execSql(request);
}
function DBData(){
var myData = null;
this.setMyData = function(obiect){
myData = obiect;
};
this.getMyData = function(){
return myData;
};
};
console.log(date.getMyData()); //WHY I GET 'NULL' HERE?
When you run that code, the order of execution will be
var date = new DBData()
console.log(date.getMyData()) // null at this point
// end of execution
The function executeStatement() gets defined, but it is not executed right away. It will be presumably executed at a later time when you call it, at which point it will populate your 'var date', hence it will display the correct data correctly when you console.log(date.getMydata()) inside the callback of executeStatement().