I have created a function to fetch values from firebase. Now the variables in which I stored result of the firebase query is only accessible inside firebase operation. However I require those variables outside the function so I created callback function to overcome this problem.
My code looks like this:
I have two firebase databases.
One is to store registered users (ref1) and another one is to store paid users(paidRef). I need to check the one who has login is a registered user or a paid user.
var paidRef=new Firebase("https://app.firebaseio.com/paidUsers");
var ref1=new Firebase("https://app.firebaseio.com/tempUser");
function checkPaidUsers(res,callback){
ref1.orderByChild('userId').equalTo(jsonData.userId).once('child_added', function(snap) {
registeredUser=true;
paidRef.on('child_added',function(snapshot) {
if(snapshot.child('userId').val()==jsonData.userId )
{
paidFlag=true;
return callback(registeredUser,paidFlag,res);
}
else
{
paidFlag=false;
return callback(registeredUser,paidFlag,res);
}
})
})
}
checkPaidUsers( res,function(registeredUser,paidFlag) {
if(registeredUser!=true)
{
newUser=true;
}
return res.send({paidFlag:paidFlag,registeredUser:registeredUser,newUser:newUser});})
This code gives error as below:
Can't set headers after they are sent.
This error is coming because the callback function is called as many times as no. of children paidRef has because in case the user is not found in paidRef database it goes to else block and execute callback function.
Whats the best possible way to solve the problem of getting all the information of registered users as well as paid users from a single callback function.
Your issue is that you are calling the callback once for every user in paidRef, which doesn't seem to be your intention.
This code should only call the callback once.
var paidRef=new Firebase("https://app.firebaseio.com/paidUsers");
var ref1=new Firebase("https://app.firebaseio.com/tempUser");
function checkPaidUsers(res,callback){
ref1.orderByChild('userId').equalTo(jsonData.userId).once('child_added', function(snap) {
registeredUser=true;
paidRef.child(jsonData.userId).once('value', function(snapshot) {
var paidFlag = false;
if (snapshot.val() !== null) {
paidFlag = true;
}
callback(registeredUser, paidFlag, res)
})
})
}
checkPaidUsers( res,function(registeredUser,paidFlag) {
if(registeredUser!=true)
{
newUser=true;
}
return res.send({paidFlag:paidFlag,registeredUser:registeredUser,newUser:newUser});})
Related
I have created a google function for firebase that when A new conversation is added the function attaches it to the Users table under a new collection for each user in the conversation but when the function gets triggered nothing happens in the database, So far I have console logged the values tp make sure they ware being set right and they ware I have also tried looking at the google function logs and there are no errors according to the logs the script ran with no errors
Here is the code for the function
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
export const onConversationCreated = functions.firestore.document("Conversations/{conversationID}").onCreate((snapshot, context) => {
let data = snapshot.data();
let conversationID = context.params.conversationID;
if(data){
let members = data.members;
for(let index = 0; index < members.length; index++){
let currentUserID = members[index];
let remainingUserIDs = members.filter((u: string) => u !== currentUserID)
remainingUserIDs.forEach((m: string) => {
return admin.firestore().collection("Users").doc(m).get().then((_doc) => {
let userData = _doc.data();
if(userData){
return admin.firestore().collection("Users").doc(currentUserID).collection("Conversations").doc(m).create({
"conversationID": conversationID,
"image": userData.image,
"unseenCount": 1,
});
}
return null;
}).catch(() => {return null})
});
}
}
return null;
});
Can someone tell me if there is something wrong with my code or do I have to give functions permission to write to the cloud firestore database?
You are not dealing with promises correctly. Your function needs to return a promise that resolves with all of the asynchronous work is complete. Right now, it's just returning null, and not waiting for any work to complete.
All Firestore operations are asynchronous and return a promise. You will need to use these promises to build a single promise to return from the function. That means each time you query and write a document, that's generating another promise to handle.
Also, you should know that there is no method create() that you're trying to use to add a document. Perhaps you meant to use set() instead. The code will crash if it tries to execute create().
I have a file called db.js from which I make all my firebase calls.
I am calling a function in db.js from another file called home.js
How do I make it that the firebase connection stays open and the data gets passed back to home.js? I can't use a promise because that closes the connection.
Here is the function from db.js:
export function getShopNames() {
let userID = auth.currentUser.uid
let stores = []
userDB.ref('/users/' + userID + "/stores").on('value', snap => {
snap.forEach(storeNames => {
stores.push(storeNames.key)
})
return stores
})
}
and I call it from home like this:
let stores = db.getShopNames()
I want it to work so if a new store gets added to the real-time database, the variable updates
There is no concept of file based scope in JavaScript. The listener will stay active from the moment you call on('value', until you either call off on that same location or until you load a new page.
But your return stores doesn't do anything meaningful right now. It returns a value from the callback function that nobody will ever see/use.
Data is loaded from Firebase asynchronously, which means you can't return it from a function in the normal way. By the time the return statement runs, the data hasn't loaded yet. That's why you'll usually return a so-called promise, which then resolves when the data has loaded.
In your function that'd be:
export function getShopNames() {
return new Promise(function(resolve, reject) {
let userID = auth.currentUser.uid
let stores = []
userDB.ref('/users/' + userID + "/stores").once('value', snap => {
snap.forEach(storeNames => {
stores.push(storeNames.key)
})
resolve(stores);
}, (error) => {
reject(error);
})
}
Now you can call this function like this:
getShopNames().then((shopnames) => {
console.log(shopnames);
})
Update: you commented that you also want to handle updates to the shop names, you can't use once() and can't use promises (since those only resolve once).
Instead pass in a custom callback, and invoke that every time the shop names change:
export function getShopNames(callback) {
let userID = auth.currentUser.uid
let stores = []
userDB.ref('/users/' + userID + "/stores").once('value', snap => {
snap.forEach(storeNames => {
stores.push(storeNames.key)
})
callback(stores);
})
}
And then call it like:
getShopnames(function(shopnames) {
console.log(shopnames);
});
I have a function that checks the existence of token in the database. The problem is it takes some amount of time to return that bool value and I kinda need to pause the function so that the function realizes that the token has existed and run the query again.
const registerToken = dispatch => {
var tokenExisted = null
do {
let token = generateRandomToken();
firebase.database().ref(`/Token`).orderByChild("token").equalTo(token).once("value", snapshot => { // check whether token exists
if (!snapshot.val()) { // if token not exist
token = false;
// register token to firebase
} else {
token = true; // continue the loop to generate a new token and query again
}
})
} while (tokenExisted === true);
}
My setup is basically a do-while loop, when the function first gets call
tokenExisted = null, then a random 4 digit token will be generated and a query will be dispatched to firebase and verify it token has existed.
If token has existed, then tokenExisted = true. I expect it the assignment to be executed but the single threaded nature of Javascript will reach the end of the loop before the query return anything.
I figured to use setTimeout and periodically add some small amount of time whenever tokenExisted = null to kinda safe guard so that the function will always catch when query function returns anything.
Has anyone had a better approach to achieve the same thing?
You might want to call the function itself recursively, as such.
const registerToken = dispatch => {
let token = generateRandomToken();
const tokenObjectRef = firebase.database().ref(`/Token`);
tokenObjectRef.orderByChild("token").equalTo(token).once("value")
.then(snapshot => {
if (!snapshot.val()) {
// success!
} else {
registerToken(dispatch) // call itself again
}
})
.catch(error => {} ))
}
The logic is that token will be refreshed during each new iteration, should the process fails and a new query is needed (if this is what you need).
Note: avoid using do-while in async logic. Plan carefully ahead as you might encounter lots of logic error and it is hard to trace.
Call the function recursively.
function get_token_then(callback_when_token_found) {
firebase.database().etc.etc(function (data) {
if (data == what_you_want) {
callback_when_token_found(data);
} else {
// You might want to wrap this in setTimeout in order to throttle your database calls
get_token_then(callback_when_token_found);
}
}
}
The following code supposed to be update username in the data base then retrieve updated username.
updateUserMame and getUserName are two different REST calls.
updateName(name) {
var obj = this;
if (name === 'None') {
name = null;
}
obj.UtilityService.updateUserName(name, obj.userId)
.success(function (data) {
if (data) {
obj.getUserName(obj.userId);
console.log('Name is updated for ID:'||obj.userId);
} else {
console.log('Something Wrong');
}
});
}
getUserName(userId){
obj.UtilityService.getUserName(userId)
.then(function (result) {
console.log(result.user.userId);
}
}
I have user name 'Nathan Drake' in the dataBase.
When I run the update function with 'Elena Fisher', it is returning 'Nathan Drake'.
I've read some articles to make synchronus service calls, but unable to figure out what is going wrong.
Please help.
You could wrap your update function in a promise:
var updatePromise = $q.when(updateName(name)); // creates a promise
When your promise has finished processing, you can resolve it using then() which takes a success callback and an error callback
updatePromise().then(function successCallback(response){ // resolves the promise using then
getUserName(userId) // execute the rest of your code
},
function errorCallback(response){
console.log(error)
});
You would need to inject $q into the scope you are working with
Your code does not make much sense, that is I see possible mistakes as it looks like you are interchanging user name and user id and calling the obj context from inside a function even when its not declared there etc. Either we are missing code or this will fail when you try to run it.
Here is your example with some fixes and comments that show how you could do it using callbacks (no sync code, as mentioned by everyone else on this thread you should avoid actually waiting for I/O and use callbacks instead).
updateName(name) {
var obj = this; // good, you captured this
if (name === 'None') {
name = null;
}
obj.UtilityService.updateUserName(name, obj.userId)
.success(function (data) {
if (data) {
// ok, you successfully updated the name so why would you go back to the server and get it again? You know the value based on your update.
console.log('Name is updated for ID:' + obj.userId.toString());
// for your example though here is how you could handle it
obj.getUserName(obj, obj.userId, function(user){ // i assumed the name is stored in variable userName
console.log('Name from server = ' + user.userName); // no idea what you are returning but you can figure it out from here
// maybe you also want to capture it again??
obj.name = user.userName;
});
} else {
console.log('Something Wrong');
}
});
}
// pass in captured this as obj, the user id, and a callback
getUserName(obj, userId, callback){
obj.UtilityService.getUserName(userId)
.then(function (result) {
callback(result); // call the callback with the result. The caller can then do something with it
}
}
I have a rather simple getUser method that I'm having some trouble with. I am not deeply familiar with scopes and such in JS so this is giving me a head ache. Basically I want to fetch an object from the database and return it to the calling method:
function getUser(uid)
{
var result = null;
var userTable = tables.getTable('Users');
userTable.where({
userId: uid
}).read({
success: function (results) {
if (results.length > 0) {
result = results[0];
console.log('userid'+result.id);
}
}
});
console.log('userid-'+result.id); // undefined!!
return result;
}
Also, returning from inside the success doesn't return from getUser, but just the function defined inside. I tried "result = function(results)" as well but it stores the defined function and not the return value.
How am I supposed to do this?
I found a solution to this elsewhere. In practice (to the best of my understanding), it is not possible to do this within a JavaScript with asynchronous functions. What you need to do is create a recursion instead from inside the success handler.
Because the call to the database is asynchronous, your last two lines are executed (and hence result is undefined) before the call the database actually finishes. So you need to handle everything inside your success callback. Or, if your getUser() func is a helper, you could structure your code (without recursion) like this with a callback:
function insertOrWhateverCallingMethod()
{
var uid = 'blah';
getUser(uid,function(user) {
// Do something with the user object
});
}
function getUser(uid,callback)
{
var result = null;
var userTable = tables.getTable('Users');
userTable.where({
userId: uid
}).read({
success: function (results) {
if (results.length > 0) {
result = results[0];
console.log('userid'+result.id);
callback(result);
}
}
});
callback(null);
}
The code above assumes you're in a table script, where the tables object is available - if it's not you can pass it as a parameter to getUser().