Async / await code not called in useEffect function - javascript

I can't figure out why the await function setDoc is not completed in my example below when I launch my react native app for the first time.
When I launch it a second time however, it works well.
Can you help me?
useEffect(() => {
registerForPushNotificationsAsync().then(async token => {
// The following gets called
console.log("Before await")
// The following does not complete when I first launch the app.
await setDoc(doc(db, "devices", token), { test: "test" })
.then(x => {
// The following does not get called
console.log('Sucess')
})
.catch(error => {
// The following does not get called
console.log('Error')
})
// The following does not get called
console.log("After await")
});
return () => {};
}, []);
with registerForPushNotificationsAsync defined outside useEffect as:
async function registerForPushNotificationsAsync() {
...
return token;
}
Thank you.

Try moving the async function outside of the useEffect function:
const someAsyncFunc = async () => {
console.log("Before await")
try {
const token = await registerForPushNotificationsAsync();
await setDoc(doc(db, "devices", token), { test: "test" })
console.log('Success')
} catch (error) {
/// do error handling
console.log(error);
}
console.log("After await")
}
useEffect(() => {
someAsyncFunc();
}, []);

use async await as follows.
useEffect(() => {
registerForPushNotificationsAsync().then(async (token) => {
// The following gets called
console.log('Before await');
try {
await setDoc(doc(db, 'devices', token), { test: 'test' });
console.log('Sucess');
} catch (error) {
console.log('Error');
}
// The following does not get called
console.log('After await');
});
return () => {};
}, []);

If there a reason why you want to use await there ?
Otherwise you should try to do this using only .then and syncronious code :
useEffect(() => {
return registerForPushNotificationsAsync().then(async token => {
// The following gets called
console.log("Before await")
// The following does not complete when I first launch the app.
return setDoc(doc(db, "devices", token), { test: "test" })
.then(x => {
// The following does not get called
console.log('Sucess')
})
.then(() => {
// The following does not get called
console.log("After await")
return () => {};
})
.catch(error => {
// The following does not get called
console.log('Error')
})
});
}, []);

Related

React-native AsyncStorage Issue

In the code snippet you see, I am trying to reach the data that I have determined through asyncStorage in the getToken and `` functions, but when I open the page with these codes from the emulator, the data is empty for the first time, and then when I do ctrl+s from the editor, the data is full. What is the reason for this problem?
App.js Page
getToken: async () => {
const token = AsyncStorage.getItem('userToken');
return token;
},
getMail: async () => {
const mail = AsyncStorage.getItem('userMail');
return mail;
},
OrderListScreen Page
getToken().then((res) => {
if(res){
setToken(res);
console.log(token)
}else {
setToken('');
}
});
getMail().then((res) => {
if(res){
setMail(res);
console.log(mail)
}else {
setMail('');
}
});
Apply await before using AsyncStorage.getItem:
getToken: async () => {
const token = await AsyncStorage.getItem('userToken');
return token;
},
getMail: async () => {
const mail = await AsyncStorage.getItem('userMail');
return mail;
},
In the log you'll not get the updated state in next line of state setter.
getToken().then((res) => {
if(res){
setToken(res);
console.log(token); //You'll never get this value here because state updates are asynchronous in React
console.log("res : ", res);
}else {
setToken('');
}
});
getMail().then((res) => {
if(res){
setMail(res);
console.log(mail)//You'll never get this value here because state updates are asynchronous in React
console.log("Email Res : ", res);
}else {
setMail('');
}
});

Why is my promise function returning before finished

In my header component:
signIn() {
signInWithPopup(auth, provider).then((result) => {
this.updateUser(result.user.uid);
const userRef = doc(db, 'users', result.user.uid);
this.firestoreUser(userRef)
.then((userDoc) => {
if (!userDoc.exists()) {
this.addNewUserToFirestore(userRef, result.user);
}
})
.then(() => {
console.log('Read user from firestore');
// FIXME: readUserFromFirestore still isn't finishing before moving on...
this.readUserFromFirestore();
})
.then(() => {
console.log('Read personal patches');
this.readPersonalPatches();
})
.then(() => {
console.log('Add watcher');
this.geolocationId = navigator.geolocation.watchPosition(
this.nearLandmark,
this.errorCallback
);
});
});
},
readUserFromFirestore:
async readUserFromFirestore({ commit, state }) {
const userRef = doc(db, 'users', state.user);
try {
const userDoc = await getDoc(userRef);
await (() => {
return new Promise((resolve) => {
for (const property in userDoc.data()) {
const propertyValue = userDoc.data()[property];
commit('addProfileProperty', {
propertyName: property,
propertyValue,
});
}
console.log(
'Just finished putting in user patches',
state.profile.patches
);
resolve();
});
})();
} catch (e) {
alert('Error!');
console.error(e);
}
},
};
readPersonalPatches:
async readPersonalPatches({ commit, state }) {
try {
if (state.user) {
// Get a copy of all the user's patches
state.ownedPatchesArray = [];
state.unownedPatchesArray = [];
await (function () {
console.log('Made it inside the await from readpersonalpatches');
return new Promise((resolve) => {
console.log('raw badges', state.rawPatches);
console.log('user badges', state.profile.patches);
state.rawPatches.forEach((patch) => {
if (JSON.stringify(state.profile.patches).includes(patch.slug)) {
commit('addToArray', {
arr: 'ownedPatchesArray',
value: patch,
});
} else {
commit('addToArray', {
arr: 'unownedPatchesArray',
value: patch,
});
}
});
resolve();
});
})();
}
} catch (error) {
alert('Error reading personal patches');
console.log(error);
}
},
Console Output:
Read user from firestore
Read personal patches
Made it inside the await from readpersonalpatches
raw badges **accurate badge list**
user badges undefined
TypeError: Cannot read properties of undefined (reading 'includes')
Add watcher
Just finished putting in user patches **accurate user patch list**
In readUserFromFirestore I wasn't sure exactly how to approach waiting on the user's patches to be added to the array before moving on in the sign-in process. One of the properties that is being looped over is profile.patches. readPersonalPatches() uses that property. But on fresh logins I get an error in readPersonalPatches() because profile.patches is undefined at that point. (On logins after cacheing I do not have an issue reading profile.patches apart from the data potentially being outdated.)
I am using Vue, Vuex, and Firebase for Authentication and Firestore.
For my purposes patch and badge are interchangeable terms.
Thanks to Luaan for educating me on how then blocks work I have it going now. I wasn't returning the promises, only calling the function and then not doing anything with the returned promises 🤦
Fixed lines:
.then((userDoc) => {
return (function () {
if (!userDoc.exists()) {
this.addNewUserToFirestore(userRef, result.user);
}
})();
})
.then(() => {
console.log('Read user from firestore');
return this.readUserFromFirestore();
})
.then(() => {
console.log('Read personal patches');
return this.readPersonalPatches();
})

Trying to execute an imported Async function but the function is not behaving asynchronously

I am using React to build a website. I have imported an asynchronous function to execute when I press a button. However, the function is not working asynchronously and I really don't understand why.
interact.js:
export const getNFT = async () => {
setTimeout(() => {
console.log('getNFT code execute');
return nft;
}, 2000);
};
const nft = {
tokenURI: 'https://gateway.pinata.cloud/ipfs/QmdxQFWzBJmtSvrJXp75UNUaoVMDH49g43WsL1YEyb',
imageURL: 'https://gateway.pinata.cloud/ipfs/QmeMTHnqdfpUcRVJBRJ4GQ2XHU2ruVrdJqZhLz',
ID: '212'
};
Main.js
import {
getNFT
} from 'interact.js';
// This function is executed when a user clicks on a button
let getAllocatedNFT = async () => {
try {
let response = await getNFT();
console.log('response from server:: '+response);
}catch(e){
console.log(e);
}
};
console:
response from server:: undefined
getNFT code execute // This is executed correctly after 2 seconds
You have to return promise which will resolve your webAPI(setTimeout)
Please use like below:
const getNFT = async () => {
return new Promise(resolve => setTimeout(() => {
console.log("getNFT code execute")
resolve(true)
}, 2000)
);
};

Can not return from a function

I have a function that looks like following
export const checkForAvailableAgent = (topicId, serviceUrl, serviceId) => {
const serviceInfo = new window.adiaLive.ServiceInfo({
topicId: topicId, // set here the topicId which you want listen for
OnError: e => {
// react to error message (optional)
console.log("error: ", e);
},
OnServiceStateChange: e => {
if (e.ConnectedAdvisers > 0) {
// there are advisers online for given topicId
console.log("studio available");
return true;
} else {
console.log("studio not available");
return false;
}
}
});
serviceInfo.connect(serviceUrl, serviceId);
};
however the return statements don't return anything when I use the function in the following manner
useEffect(() => {
const agent = checkForAvailableAgent(
`sales_${i18n.language}`,
"https://linktoserviceurl",
"serviceid"
);
// console.log("studio available is: ", agent);
}, []);
the console.log massages appear but the return statement is undefined.
any help would be appreciated.
You can not return from a callback function, as it is running asynchronously and you are not waiting for it to have a result ready.
You can however make the function itself async by returning a Promise instead of the actual result and wait until the Promise has a result ready (e.g. it is resolved):
export const checkForAvailableAgent = (topicId, serviceUrl, serviceId) => {
return new Promise((resolve, reject) => {
const serviceInfo = new window.adiaLive.ServiceInfo({
topicId: topicId, // set here the topicId which you want listen for
OnError: e => {
// react to error message (optional)
console.log("error: ", e);
reject(); // reject on failure
},
OnServiceStateChange: e => {
if (e.ConnectedAdvisers > 0) {
// there are advisers online for given topicId
console.log("studio available");
resolve(true); // resolve instead of return
} else {
console.log("studio not available");
resolve(false);
}
}
});
serviceInfo.connect(serviceUrl, serviceId);
})
};
useEffect(() => {
checkForAvailableAgent(
`sales_${i18n.language}`,
"https://linktoserviceurl",
"serviceid"
).then((agent) => { // then callback is called when the promise resolved
console.log("studio available is: ", agent);
}).catch(error => { // catch is called when promise got rejected
console.log('An error happened');
});
}, []);
The function servceInfo.OnServiceStateChange is a function into the object (seems to be an event).
I'd suggest declaring a variable on the checkForAvailableAgent like connected and change it's value when the event is called.
Then access it using checkForAvailableAgent.connected.
A version with async/await and try/catch
export const checkForAvailableAgent = (topicId, serviceUrl, serviceId) => {
return new Promise((resolve, reject) => {
const serviceInfo = new window.adiaLive.ServiceInfo({
topicId: topicId,
OnError: reject,
OnServiceStateChange: e => resolve(e.ConnectedAdvisers > 0)
});
serviceInfo.connect(serviceUrl, serviceId);
})
};
useEffect(() => {
(async () => {
try {
const isAvailable = await checkForAvailableAgent(
`sales_${i18n.language}`,
"https://linktoserviceurl",
"serviceid"
);
// console.log("Result", isAvailable)
} catch(e) {
console.error(e)
}
})()
// console.log("studio available is: ", agent);
}, []);
There are 2 possible reasons
you are not returning anything from checkForAvailableAgent.
After returning from the checkForAvailableAgent, it might be asynchronous function. You can use async & await.

Test Javascript calls to api with jest

I am trying to test the call to github api using jest to see if the results are returned (the aim of this is to test my unit testing skills). But for some reasons, my code works fine but still fails my test. My suspicion is that i most likely don't understand how to write these kind of test. Below is my code
const functions = {
getUserRepo: async (username) => {
const url = `https://api.github.com/users/${username}/repos`;
console.log(url);
let result = [];
await axios.get(url)
.then(function (response) {
response.data.forEach(value => result.push(value.name));
return result;
})
.catch(function (error) {
return error;
});
}
}
This code above returns the right results in an array format but fails the test below
describe('Check repos from git api', () => {
test('Should return user repos', async () => {
await functions.getUserRepo('whitehox')
.then((response) => {
expect(response.data).toEqual([ '57','decafreelance','decases','eexport','exportchat','flisch', 'gitprac', 'itravelcentral', 'pollark', 'portfolio', 'startereit', 'talkative', 'team-portfolio'])
})
});
});
Please what is the issue with this test and how do i fix it?
Two things need to be fixed.
You need to return the result from your function. It can be simplified to this:
const functions = {
getUserRepo: (username) => {
const url = `https://api.github.com/users/${username}/repos`;
console.log(url);
return axios.get(url) // <= return the result
.then(function (response) {
return response.data.map(value => value.name);
})
.catch(function (error) {
return error;
});
}
}
...which makes response the array so test it directly:
describe('Check repos from git api', () => {
test('Should return user repos', async () => {
await functions.getUserRepo('whitehox')
.then(response => {
// response **is** the array
expect(response).toEqual(['57', 'decafreelance', 'decases', 'eexport', 'exportchat', 'flisch', 'gitprac', 'itravelcentral', 'pollark', 'portfolio', 'startereit', 'talkative', 'team-portfolio', 'YorubaIndigenous']); // Success!
})
});
});
(...and there is also a new repo called 'YorubaIndigenous', I added it to the expected value).

Categories