Handling Async Await to use Firebase with react native - javascript

const userID = firebase.auth().currentUser.uid
const checkRoomExists = async ()=>{
var queryRef = firestore.collection("groups");
try{
console.log("userId: "+userID)
var snapshot = await queryRef.where('participant', '==',userID).where('host','==',findParticipant).get()
if (snapshot.empty) {
console.log('No matching documents1.');
return;
}
else{
snapshot.forEach(doc => {
setChatroomAlready(doc.id)
})}
}
catch(e){
console.log('Error getting documents', e);
}
finally{
console.log("chatroomAlready :"+chatroomAlready)
}
}
async function performCreateGroup () {
console.log("1 :"+findParticipant)
checkRoomExists();
console.log("2 :"+chatroomAlready)
}
//first call
1: ZUxSZP09fzRzndr7vhK8wr56j3J3
2: (blank)
//second call returns what I expected exactly
1: ZUxSZP09fzRzndr7vhK8wr56j3J3
2: zQN4hbgqjKhHHHc70hPn
This is my code and variable chatroomAlready returns ''
But if I fast-refresh my app with react-native's function, It returns the expected value very well
I think this problem happens because my less understanding of async-await.
Any helps or clues can I solve this problem?

i would rather use the following syntaxe:
queryRef.where('participant', '==',userID).where('host','==',findParticipant).get().then((snapshot)=>(
DO YOUR STUFF))
Since queryRef act as a promise.

Related

Having issues with Await not waiting using async/await

I have a vue.js project and am using Vuex for my store. I am trying to process all notifications to users within the store and I am having some issues with async/await.
I am sure its something very simple and trivial but I am stuck. Any help is much appreciated.
Here is my function
async getNotifications() {
console.log('1')
const internalNotifications = await this.$store.getters['AppData/getInternalNotifications']
console.log('2')
if(internalNotifications) {
this.notifications = internalNotifications
this.message = true
console.log('4 ', internalNotifications)
}
}
Here is my function in the store to get the notifications and dispatch them.
async getInternalNotifications({ dispatch }, { cid, user, roles, isSupperAdmin }) {
console.log('getInternalNotifications')
let internalNotifications = []
// Get all the notifications for this church
let getAllNotifications = await db
.collection('notifications')
.where('cid', '==', cid)
.where('active', '==', true)
.orderBy('created')
.get()
for (const notificationDoc of getAllNotifications.docs) {
let notification = notificationDoc.data()
notification.id = notificationDoc.id
// check to make sure this notification has not already been read
let getAllReadNotifications = await db
.collection('notificationsread')
.where('notificationid', '==', notification.id)
.where('userid', '==', user.uid)
.get()
if (getAllReadNotifications.empty)
internalNotifications.push(notification)
}
if (!isSupperAdmin && internalNotifications.length > 0) {
const hasAudience = internalNotifications.filter((el) => {
return roles.some(r => el.audience.includes(r))
})
hasAudience.sort((a, b) => (a.created < b.created) ? 1 : -1)
internalNotifications = hasAudience[0]
}
console.log('3 ', internalNotifications)
dispatch('addInternalNotification', internalNotifications)
},
My thinking is when viewing the console log I should see the logs in order 1,3,2,4 but instead I get 1,2,4,3 and as you can see from the screen shot it's an Observer not the actual array/object.
see screen shot of console log
What appears to be happening, and looking at the order of logs, is that this.$store.getters does not return a Promise and therefore adding await does nothing.
Make sure that this.$store.getters is actually returning a Promise that can be awaited or look at using Actions. From the docs if you wanted to use an action for this, you could try something like the following:
NOTE All of this is untested and I don't know vue, so use with caution knowing changes may be needed and best practices may not be getting followed by this approach.
actions: {
async getInternalNotifications({ dispatch }) {
return new Promise((resolve, reject) => {
console.log('getInternalNotifications')
let internalNotifications = []
// Get all the notifications for this church
let getAllNotifications = await db
.collection('notifications')
.where('cid', '==', cid)
.where('active', '==', true)
.orderBy('created')
.get()
for (const notificationDoc of getAllNotifications.docs) {
let notification = notificationDoc.data()
notification.id = notificationDoc.id
// check to make sure this notification has not already been read
let getAllReadNotifications = await db
.collection('notificationsread')
.where('notificationid', '==', notification.id)
.where('userid', '==', user.uid)
.get()
if (getAllReadNotifications.empty)
internalNotifications.push(notification)
}
if (!isSupperAdmin && internalNotifications.length > 0) {
const hasAudience = internalNotifications.filter((el) => {
return roles.some(r => el.audience.includes(r))
})
hasAudience.sort((a, b) => (a.created < b.created) ? 1 : -1)
internalNotifications = hasAudience[0]
}
console.log('3 ', internalNotifications)
dispatch('addInternalNotification', internalNotifications)
resolve(internalNotifications)
// TODO: Reject error scenarios, e.g. wrap you db calls in a try...catch and reject the promise with the error
})
}
async getNotifications() {
console.log('1')
const internalNotifications = await this.$store.dispatch('AppData/getInternalNotifications')
console.log('2')
if(internalNotifications) {
this.notifications = internalNotifications
this.message = true
console.log('4 ', internalNotifications)
}
}
I was working on a web app recently and I think I can help. Someone correct me if I'm wrong, but typically after calling an async function, I follow it with a .then. Example:
const myPromise = anAsyncFunction() // should return a promise
myPromise.then (
function(value) {
// function to be executed when the async function is finished
},
function(error) {
// error handling
}
)
Otherwise, my app would not wait for the async function to finish and would not have the right variable in time to process it.

Attempting to convert two promise functions using async/await

This is the prompt I am given:
Fortune teller functions
You'll be using the following functions provided in the ./utils/fortune-teller.js file. This library is just a way for you to practice working with promises. While you can see the source code in the file, you do not need to understand it fully or make any changes to it.
welcome()
The welcome() function returns a promise that, when resolved, will return a short welcome string.
Example:
welcome().then((response) => {
console.log(response);
// "Provide me a question and I'll give you an answer..."
});
goodbye()
The goodbye() function returns a promise that, when resolved, will return a short goodbye string.
Example:
goodbye().then((response) => {
console.log(response);
// "Best of luck in the future..."
});
tell()
The tell() function takes a string and returns a promise that, when resolved, will return a random fortune.
Example:
tell("Will the weather be warm today?").then((response) => {
console.log(response);
// "Outlook good."
});
If no argument is provided, then the function will return a rejection that returns a short error message.
Example:
tell().catch((response) => {
console.log(response);
// "A question is required..."
});
Functions to complete
Update the getFortune() and fullSession() functions to use async and await as well as try and catch. Note that these functions require you to log statements with console.log() and should continue to do so.
Here is what I have tried:
const { welcome, goodbye, tell } = require("../utils/fortune-teller");
async function getFortune(question) {
tell(question).then(msg => {
await console.log(`Your question was: ${question}`);
await console.log(`Your fortune is: ${msg}`)})
.catch(err => await console.log('There was an error: A question is required...'));
}
async function fullSession(question) {
welcome().then(msg => await console.log(msg))
getFortune(question)
if (!question) {
return;
}
goodbye().then(msg => console.log(msg));
}
module.exports = { getFortune, fullSession };
The question also requires that you use "await" at least 4 times, and that you are not to use any .then or .catch statements. So I have also tried:
const { welcome, goodbye, tell } = require("../utils/fortune-teller");
async function getFortune(question) {
try{
let response = await tell(question)
await console.log(`Your question was: ${question}`);
await console.log(`Your fortune is: ${response}`);
} catch (error) {
await console.log(`There was an errror: ${error}`);
}
}
async function fullSession(question) {
try {
let welcom = welcome();
let fortune = getFortune(question);
let bye = goodbye();
let responses = await Promise.all([welcom, fortune, bye])
console.log(responses[0]);
console.log(responses[1]);
console.log(responses[2]);
} catch (error) {
console.log(`There was an error: ${error}`);
}
}
module.exports = { getFortune, fullSession };
I encounter more issues with the second way than I do with the first its just eliminating the .catch and .then statements that's tripping me up because it seems like no matter what adjustments I make it only makes it run worse. Any help would be appreciated. Thanks!
Inlcuded here is also the fortune-teller.js file if anyone is interested in it:
const responses = require("./responses.json");
function selectRandomFortune() {
const num = Math.random() * responses.length;
const index = Math.floor(num);
return responses[index];
}
function welcome() {
const message = "Provide me a question and I'll give you an answer...";
return Promise.resolve(message);
}
function goodbye() {
const message = "Best of luck in the future...";
return Promise.resolve(message);
}
function tell(question) {
if (!question) {
const message = "A question is required...";
return Promise.reject(message);
}
const fortune = selectRandomFortune();
return Promise.resolve(fortune);
}
module.exports = { welcome, goodbye, tell };
here you go, exactly 4 awaits where they actually need to be
const { welcome, goodbye, tell } = require("../utils/fortune-teller");
async function getFortune(question) {
try {
const fortune = await tell(question);
console.log(`Your question was: ${question}`);
console.log(`Your fortune is: ${fortune}`);
} catch (err) {
// tell rejects with "A question is required..." so just log that
console.log(err);
}
}
async function fullSession(question) {
try {
const msg = await welcome();
console.log(msg);
await getFortune(question);
const response = await goodbye()
console.log(response);
} catch (err) {
console.log(err);
}
}
module.exports = { getFortune, fullSession };
Alternative fullSession
async function fullSession(question) {
try {
console.log(await welcome());
await getFortune(question);
console.log(await goodbye());
} catch (err) {
console.log(err);
}
}
so i switched a couple things around, 'getFortune' it seems did require a try/catch but now the only spot where its hung up is on goodbye() the response comes back as undefined
const { welcome, goodbye, tell } = require("../utils/fortune-teller");
async function getFortune(question) {
try {
const msg = await tell(question);
console.log(`Your question was: ${question}`);
console.log(`Your fortune is: ${msg}`);
} catch (err) {
console.log('There was an error: A question is required...');
}
}
async function fullSession(question) {
try {
const msg = await welcome();
console.log(msg);
const fortune = await getFortune(question);
console.log(`Your fortune is: ${fortune}`);
const response = await goodbye();
console.log(response);
} catch (err) {
console.log(err);
}
}
module.exports = { getFortune, fullSession };

Returning boolean from mongoose .exists function

I'm trying to learn some node.js and mongoDB. I'm writing a demo login system to that end.
I have a function called by my main api to check in the mongoDB whether a refreshToken exists.
const checkRefreshTokenDB = (tokenPayload) => {
const decoded = jwt.decode(tokenPayload);
RefreshToken.exists(
{ tokenId: decoded.refresh.tokenId },
function (err, result) {
if (err) {
console.log(err);
} else {
console.log('Result: ' + result);
}
}
);
};
I can see the RefreshToken.exists is working find in the console.log. What I'm struggling with is making this function return a simple true/false to the caller. At the moment it seems to be returning a promise.
Any help would be very appreciated
Thanks
One way to resolve the issue is by using async/await.
const checkRefreshTokenDB = async (tokenPayload) => {
const decoded = jwt.decode(tokenPayload);
return await RefreshToken.exists({ tokenId: decoded.refresh.tokenId });
};
Now you can call this function as shown below from an async function (await is only valid in async function)
const status = await checkRefreshTokenDB(tokenPayload);
console.log(status)
If it really is returning a promise, then use async/await:
const checkRefreshTokenDB = (tokenPayload) => {
const decoded = jwt.decode(tokenPayload);
RefreshToken.exists(
{ tokenId: decoded.refresh.tokenId }
, async function (err, result) { console.log(await result); });
};

Unable to return a value from an async function using firestore

I am new to async and am trying to return a value from a Firestore db using node.
The code does not produce any errors, nor does it produce any results!
I want to read the db, get the first match and return this to the var country.
const {Firestore} = require('#google-cloud/firestore');
const db = new Firestore();
async function getCountry() {
let collectionRef = db.collection('groups');
collectionRef.where('name', '==', 'Australia').get()
.then(snapshot => {
if (snapshot.empty) {
console.log('No matching documents.');
return "Hello World";
}
const docRef = snapshot.docs[0];
return docRef;
})
.catch(err => {
console.log('Error getting documents', err);
});
}
let country = getCountry();
When you declare an function async, that means it always returns a promise. It's generally expected that the code inside it will use await to deal with other promises generated within that function. The final returned promise will resolve with the value returned by the function.
First of all, your async function should look more like this:
async function getCountry() {
let collectionRef = db.collection('groups');
const snapshot = await collectionRef.where('name', '==', 'Australia').get()
if (snapshot.empty) {
console.log('No matching documents.');
// you might want to reconsider this value
return "Hello World";
}
else {
return snapshot.docs[0];
})
}
Since it returns a promise, you would invoke it like any other function that returns a promise:
try {
let country = await getCountry();
}
catch (error) {
console.error(...)
}
If you can't use await in the context of your call to getCountry(), you will have to handle it normally:
getCountry()
.then(country => {
console.log(country);
})
.catch(error => {
console.error(...)
})
The moment you sign up to use async/await instead of then/catch, things become much different. I suggest reading up more on how it works.

Firebase: Transaction with async/await

I'm trying to use async/await with transaction.
But getting error "Argument "updateFunction" is not a valid function."
var docRef = admin.firestore().collection("docs").doc(docId);
let transaction = admin.firestore().runTransaction();
let doc = await transaction.get(docRef);
if (!doc.exists) {throw ("doc not found");}
var newLikes = doc.data().likes + 1;
await transaction.update(docRef, { likes: newLikes });
The above did not work for me and resulted in this error: "[Error: Every document read in a transaction must also be written.]".
The below code makes use of async/await and works fine.
try{
await db.runTransaction(async transaction => {
const doc = await transaction.get(ref);
if(!doc.exists){
throw "Document does not exist";
}
const newCount = doc.data().count + 1;
transaction.update(ref, {
count: newCount,
});
})
} catch(e){
console.log('transaction failed', e);
}
If you look at the docs you see that the function passed to runTransaction is a function returning a promise (the result of transaction.get().then()). Since an async function is just a function returning a promise you might as well write db.runTransaction(async transaction => {})
You only need to return something from this function if you want to pass data out of the transaction. For example if you only perform updates you won't return anything. Also note that the update function returns the transaction itself so you can chain them:
try {
await db.runTransaction(async transaction => {
transaction
.update(
db.collection("col1").doc(id1),
dataFor1
)
.update(
db.collection("col2").doc(id2),
dataFor2
);
});
} catch (err) {
throw new Error(`Failed transaction: ${err.message}`);
}
IMPORTANT: As noted by a couple of the users, this solution doesn't use the transaction properly. It just gets the doc using a transaction, but the update runs outside of it.
Check alsky's answer. https://stackoverflow.com/a/52452831/683157
Take a look to the documentation, runTransaction must receive the updateFunction function as parameter. (https://firebase.google.com/docs/reference/js/firebase.firestore.Firestore#runTransaction)
Try this
var docRef = admin.firestore().collection("docs").doc(docId);
let doc = await admin.firestore().runTransaction(t => t.get(docRef));
if (!doc.exists) {throw ("doc not found");}
var newLikes = doc.data().likes + 1;
await doc.ref.update({ likes: newLikes });
In my case, the only way I could get to run my transaction was:
const firestore = admin.firestore();
const txRes = await firestore.runTransaction(async (tx) => {
const docRef = await tx.get( firestore.collection('posts').doc( context.params.postId ) );
if(!docRef.exists) {
throw new Error('Error - onWrite: docRef does not exist');
}
const totalComments = docRef.data().comments + 1;
return tx.update(docRef.ref, { comments: totalComments }, {});
});
I needed to add my 'collection().doc()' to tx.get directly and when calling tx.update, I needed to apply 'docRef.ref', without '.ref' was not working...

Categories