Value arriving late? “Value below was evaluated just now” - javascript

let currComp = this;
let projects = []
let dataArr = []
async function getData() {
let getProject =
await axios.get('url', {
auth: {
username: 'username',
password: 'pw'
}
})
let projects = await getProject.data.value;
let arr = []
let KPIs = []
projects.map(project => {
let item = () => axios.get(`url`, {
auth: {
username: 'username',
password: 'pw'
}
})
arr.push(item)
console.log('arr', arr)
})
let results = await axios.all(arr)
results.map(result => {
result().then(function(v) {
KPIs.push(v.data.value)
})
})
}
getData();
What I am trying to do is:
get an axios call to fetch the name of the projects.
Use those fetched names to call multiple axios calls. This is supposed to give me some data from that project.
results = await axios.all(arr) contains functions that gives me the responses to the API call.
The responses gets pushed in the the array KPIs.
When I try to console.log('KPIs', KPIs) at the end, it gives me
which seems to be an empty array, but when I open it,
it actually has the value that I want.
The problem is, when I try to use this array in my code, it only gives me the first item of the array.
I tried to search this issue online, and it only told me that the value is arriving late.
I want to use the full array with the full result.
How can I fix it?

results = await axios.all(arr) contains functions that gives me the responses to the API call.
Yes, but that's pointless. You don't want functions that return promises for responses, and there's no use to call axios.all on array of functions or await that.
You will instead need to build an array of promises, and all() and await that.
You shouldn't need to use then either. You can simplify your code a lot:
async function getData() {
const getProject = await axios.get('url', {
auth: {
username: 'username',
password: 'pw'
}
});
const projects = getProject.data.value;
const promises = projects.map(project =>
axios.get(`url`, {
auth: {
username: 'username',
password: 'pw'
}
})
);
const results = await axios.all(promises)
const KPIs = results.map(v => v.data.value);
}

Related

How to await admin.auth().getUser() method within a forEach loop?

I am trying to iterate over an array of comments and need to grab the commenter's uid for each comment. I am a beginner to JavaScript and need a little bit of help with the following use case:
I need to grab the uid for each comment and then run a .getUser() method which will return the user's email address that is associated with the user's uid. Since .getUser() returns a promise (method reference link), I need to await somewhere in this loop. How to do so? Is this even a good approach?
(Note: My end goal is to eventually attach the email addresses to a to property in a msg object where I will then send out email notifications.)
Example data for comments:
[
{
id: 1,
uid: 'RGaBbiui'
},
{
id: 2,
uid: 'ladfasdflal'
},
{
id: 3,
uid: 'RGaBbiui'
},
{
id: 4,
uid: 'RGaBbiui'
},
{
id: 5,
uid: 'ladfasdflal'
},
{
id: 6,
uid: 'ladfasdflal'
}
]
Cloud function example:
export const sendCommentNotification = functions.firestore
.document('users/{uid}/posts/{postId}/comments/{commentId}')
.onCreate(async (snapshot, context) => {
try {
const commentsQuery = await admin
.firestore()
.collection(
`users/${context.params.uid}/posts/${context.params.postId}/comments`
)
.get()
const commentsArr = []
commentsQuery.forEach((documentSnapshot) =>
commentsArr.push(documentSnapshot.data())
)
const commentsArrUids = new Set(commentsArr.map((c) => c.uid))
console.log(commentsArrUids)
const emailAddresses = []
commentsArrUids.forEach((uid) =>
emailAddresses.push(admin.auth().getUser(uid)) // how to use await here?
)
...
const msg = {
to: //TO DO..put email addresses here..
...
You cannot use await in a forEach loop. You could use await in a for-loop but they won't run simultaneously as in Promise.all().
You can just await all the promises at once using Promise.all():
Returned values will be in order of the Promises passed, regardless of completion order.
const emailAddresses = []
commentsArrUids.forEach((uid) => {
emailAddresses.push(admin.auth().getUser(uid))
})
const data = await Promise.all(emailAddresses)
Data will be an array of UserRecord.
Then you can use the .map() method to get an array of all the emails.
const emails = data.map((user) => user.email)
The code can be written like this to make it easier:
const commentsDocs = await admin.firestore().collection(`users/${context.params.uid}/posts/${context.params.postId}/comments`).get()
const userIds = commentsDocs.docs.map(comment => comment.userId)
const usersReq = userIds.map(u => admin.auth().getUser(u.uid))
const emails = (await Promise.all(usersReq))).map((user) => user.email)
Use for loop instead.
for (let i = 0; i < commentsArrUids.length; i++) {
let user = await new Promise((resolve, reject) => {
admin.auth().getUser(commentsArrUids[i]).then((user) => {
resolve(user);
});
};
emailAddresses.push(user);
}
I will replace forEach with for of and the promises is in series. Also, I rewrite some of your codes as they are redundant.
export const sendCommentNotification = functions.firestore
.document("users/{uid}/posts/{postId}/comments/{commentId}")
.onCreate(async (snapshot, context) => {
try {
const comments = await admin
.firestore()
.collection(
`users/${context.params.uid}/posts/${context.params.postId}/comments`
)
.get();
const uids = new Set();
for (const comment of comments) {
uids.add(comment.data().uid);
}
const emailAddresses = [];
for (const uid of uids) {
const res = await admin.auth().getUser(uid);
emailAddresses.push(res);
}
} catch (err) {
console.log(err);
}
});

React JS multiple API calls, data undefined or unexpected reserved word 'await' mapping through the data:

I'm creating a JS function that will make a call to an API, loop through the returned data and perform another call to retrieve more information about the initial data (for example where the first call return an ID, the second call would return the name/address/number the ID corresponds to). Positioning the async and await keywords though, have proven to be way more challenging than I imagined:
useEffect(() => {
const getAppointments = async () => {
try {
const { data } = await fetchContext.authAxios.get('/appointments/' + auth.authState.id);
const updatedData = await data.map(value => {
const { data } = fetchContext.authAxios.get('/customerID/' + value.customerID);
return {
...value, // de-structuring
customerID: data
}
}
);
setAppointments(updatedData);
} catch (err) {
console.log(err);
}
};
getAppointments();
}, [fetchContext]);
Everything get displayed besides the customerID, that results undefined. I tried to position and add the async/await keywords in different places, nothing works. What am I missing?
map returns an array, not a promise. You need to get an array of promises and then solve it (also, if your way worked, it would be inefficient waitting for a request to then start the next one.)
const promises = data.map(async (value) => {
const { data } = await fetchContext.authAxios.get('/customerID/' + value.customerID);
return {
...value,
customerID: data
};
});
const updatedData = await Promise.all(promises);

Unable to await Axios call multiple times

Can someone explain to me why I am unable to perform consecutive axios API calls this way? I figured that since each call was awaited, then both calls would simply be treated the same way.
When I run this code, I get the following error:
TypeError: parsedEmp is not iterable at Object.listEmployees
I do not receive the same error for the Axios call above it. Here's my code:
async function listEmployees() {
const { data } = await axios.get('https://gist.githubusercontent.com/graffixnyc/febcdd2ca91ddc685c163158ee126b4f/raw/c9494f59261f655a24019d3b94dab4db9346da6e/work.json')
const parsedData = data
emp_data = []
for(comp of parsedData) {
emp_data.push([comp['company_name'],comp['employees']])
}
const { data2 } = await axios.get('https://gist.githubusercontent.com/graffixnyc/31e9ef8b7d7caa742f56dc5f5649a57f/raw/43356c676c2cdc81f81ca77b2b7f7c5105b53d7f/people.json')
const parsedEmp = data2
rosters = []
for(entry of parsedEmp) {
if(entry['id'] == e) {
company.employees.push({ first_name: entry['first_name'], last_name: entry['last_name']})
}
}
return rosters
}
Using destructuring assignment requires you to use the property names present in the source object, otherwise the resulting variables will be undefined. The Axios response object does not have a data2 property.
You would need something like this to access the data property
const response2 = await axios.get('https://...')
const parsedEmp = response2.data
or you can use this format to name the assigned variable
const { data: data2 } = await axios.get(...)
const parsedEmp = data2
or even this to save an extra line each time
const { data: parsedData } = await axios.get(/* first URL */)
// ...
const { data: parsedEmp } = await axios.get(/* second URL */)
You can have the code like below
async function listEmployees() {
const { data: parsedData } = await axios.get('https://gist.githubusercontent.com/graffixnyc/febcdd2ca91ddc685c163158ee126b4f/raw/c9494f59261f655a24019d3b94dab4db9346da6e/work.json');
const { data: parsedEmp } = await axios.get('https://gist.githubusercontent.com/graffixnyc/31e9ef8b7d7caa742f56dc5f5649a57f/raw/43356c676c2cdc81f81ca77b2b7f7c5105b53d7f/people.json');
// get the employee details
let emp_data = [];
_.map(parsedData, (data) => {
emp_data.push(data.company_name, data.employees);
});
// get the roster data
let rosters = [];
_.map(parsedEmp, (data) => {
roster.push({ first_name: data['first_name'], last_name: data['last_name']});
});
return rosters;
}

Async reduce returning promises

I have an array of objects and I have to add one property on each of the objects coming from and async function
I am doing an Array.reduce to iterate on each of the elements and return just one result: One array of objects with the new property.
I have this
const res = await resultOne.reduce(async (users = [], user, i) => {
let userName;
try {
let { name } = await names.getNames(user.id);
userName = name;
} catch (error) {
throw error;
}
delete user.id;
users.push({ ...user, userName });
return users;
}, []);
But I get the message
Push is not a function of users
And this is because I think is a promise.
How can I handle async requests in a reduce or a map
Yes, users is a promise. Don't use reduce with async functions. You could do something like await users in the first line, but that would be pretty inefficient and unidiomatic.
Use a plain loop:
const users = [];
for (const user of resultOne) {
const { name } = await names.getNames(user.id);
delete user.id;
users.push({ ...user, userName: user });
}
or, in your case where you can do everything concurrently and create an array anyway, the map function together with Promise.all:
const users = await Promise.all(resultOne.map(async user => {
const { name } = await names.getNames(user.id);
delete user.id;
return { ...user, userName: user };
}));
Because it's an async function, every time you return something it gets wrapped in a promise. To fix this you need to set the starting array as a promise and then await for the accumulator on each iteration.
const res = await resultOne.reduce(async (users, user, i) => {
try {
return [
...await users,
{ ...user, userName: await names.getNames(user.id.name) }
]
} catch (error) {
console.log(error)
}
}, Promise.resolve([]));

"Exit promise" with multiples fetch requests

I need to merge data from API. I do a first call to an endpoint that gives me a list of ids, then I do a request for each id. My goal is to return a list with the responses of all requests but I lost myself in promises ...
My code runs on NodeJS. Here is the code :
const fetch = require('node-fetch')
const main = (req, res) => {
fetch('ENDPOINT_THAT_GIVES_LIST_OF_IDS')
.then(response => response.json())
.then(response => {
parseIds(response)
.then(data => {
console.log(data)
res.json(data)
// I want data contains the list of responses
})
})
.catch(error => console.error(error))
}
const getAdditionalInformations = async function(id) {
let response = await fetch('CUSTOM_URL&q='+id, {
method: 'GET',
});
response = await response.json();
return response
}
const parseIds = (async raw_ids=> {
let ids= []
raw_ids.forEach(function(raw_id) {
let informations = {
// Object with data from the first request
}
let additionalInformations = await
getAdditionalInformations(raw_id['id'])
let merged = {...informations, ...additionalInformations}
ids.push(merged)
})
return ids
})
main()
I get this error : "await is only valid in async function" for this line :
let additionalInformations = await getAdditionalInformations(raw_id['id'])
Help me with promise and async/await please.
You're almost there, just a slight bit of error here with your parentheses:
// notice the parentheses'
const parseIds = async (raw_ids) => {
let ids= []
raw_ids.forEach(function(raw_id) {
let informations = {
// Object with data from the first request
}
let additionalInformations = await getAdditionalInformations(raw_id['id'])
let merged = {...informations, ...additionalInformations}
ids.push(merged)
})
return ids
}
You are missing an async after forEach
const parseIds = (async raw_ids=> {
let ids= []
raw_ids.forEach(async function(raw_id) {
let informations = {
// Object with data from the first request
}
let additionalInformations = await
getAdditionalInformations(raw_id['id'])
let merged = {...informations, ...additionalInformations}
ids.push(merged)
})
return ids
})
One suggestion: you are mixing promises (.then()) with async/await. Prefer async/await is more readable.
Note that getAdditionalInformations inside forEach doesn't wait for it to be done before going to the next entry of the array.
You can use plain old for(var i=0; .... instead

Categories