async/await loop array.push issue - javascript

I'm currently working on something where I need to get async data and push it to an array. Please see below.
const getStudent = async (id: string) => {
const student = await fetchSomeData(id);
const markedEssayArray: string[] = [];
for await(const essayName of student.essay) {
const markedEssay = await fetchMarkedEssay(essayName);
markedEssayArray.push(markedEssay)
};
student.essay = markedEssayArray;
return student;
};
so basically, I fetch a student, and update the student.essay array with the marked versions. However when I test the code, the student.essay array returns an empty array [].
Could anyone kindly advise where I went wrong?

You can try like this :
const getStudent = async (id: string) => {
const student = await fetchSomeData(id);
let markedEssayArray: string[] = [];
const essayArr = student.essay;
essayArr.forEach((singleEssay) => {
let markedEssay = await fetchMarkedEssay(essayName);
markedEssayArray.push(markedEssay)
}
student.essay = [...markedEssayArray];
return student;
};

Related

How to access individual object in array using Javascript

Hi I have exported using data (hawkers collection) using getDocs() from Firebase.
After that I put each hawker data as an object in an array called allStall as shown in the screenshot of the console log below.
Question 1 - How do I access each individual object in my allStall array. I try to use .map() to access each of it, but i am getting nothing.
Do note that I already have data inside my allStall array, see screenshot above.
[Update] map doesn't work in code below because field is stallname not stallName. However, it needs to be async + await if using/call in/from other function.
Question 2 - Why is there [[Prototype]]: Array(0) in my allStall array
export /*Soln add async*/function getAllStall(){
var allStall = [];
try
{
/*Soln add await */getDocs(collection(db, "hawkers")).then((querySnapshot) =>
{
querySnapshot.forEach((doc) =>
{
var stall = doc.data();
var name = stall.stallname;
var category = stall.category;
var description = stall.description;
var stallData = {
stallName:name,
stallCategory:category,
stallDescription:description
};
allStall.push(stallData);
});});
console.log(allStall);
//Unable to access individual object in Array of objects
allStall.map(stall =>{console.log(stall.stallName);});}
catch (e) {console.error("Error get all document: ", e);}
return allStall;
}
In my main js file, i did the following:
useEffect(/*Soln add await*/() =>
{
getAllStall();
/*Soln:replace the statement above with the code below
const allStall = await getAllStall();
allStall.map((stall)=>console.log(stall.stallname));
*/
}
);
You are getting nothing because allStall is empty since you are not waiting for the promise to be fullfilled
try this
export const getAllStall = () => getDocs(collection(db, "hawkers"))
.then((querySnapshot) =>
querySnapshot.map((doc) =>
{
const {stallName, category, description} = doc.data();
return {
stallName:name,
stallCategory:category,
stallDescription:description
};
});
)
try to change use effect like this
useEffect(async () =>
{
const allStats = await getAllStall();
console.log(allStats)
allStats.forEach(console.log)
}
);
A very big thanks to R4ncid, you have been an inspiration!
And thank you all who commented below!
I managed to get it done with async and await. Latest update, I figure out what's wrong with my previous code too. I commented the solution in my question, which is adding the async to the function and await to getDocs.
Also map doesn't work in code above because field is stallname not stallName. However, it needs to be async + await if using in/calling from other function.
Helper function
export async function getAllStall(){
const querySnapshot = await getDocs(collection(db, "hawkers"));
var allStall = [];
querySnapshot.forEach(doc =>
{
var stall = doc.data();
var name = stall.stallname;
var category = stall.category;
var description = stall.description;
var stallData = {
stallName:name,
stallCategory:category,
stallDescription:description
};
allStall.push(stall);
}
);
return allStall;
}
Main JS file
useEffect(async () =>
{
const allStall = await getAllStall();
allStall.map((stall)=>console.log(stall.stallname));
}
);
Hurray

Firebase async scheduler issue

I'm new to both firebase and async javascript. I'm trying to create a scheduled task in firebase to fetch a bunch of rss URLs from a collection, parse it and store it inside another collection but I'm getting Error: 4 DEADLINE_EXCEEDED: Deadline exceeded at Object.callErrorFromStatus upon its execution.
const refreshRSS = functions.pubsub.schedule('every 30 mins').onRun(async context => {
let newRSS = addRSS();
return await newPodcasts;
});
addRSS = async () => {
const newRSSFeed = new Array();
let rssURLs = await db.collection('rssURLs').get();
rssURLs.forEach(async rssURLsObject=>{
rss = rssURLsObject.data();
let rssData = await parser.parseURL(rss.url);
newRSSFeed.push(db.collection('rss').doc(encodeURIComponent(rss.url))
.set(podcast));
})
return newRSSFeeds;
}
I also tried returning Promise.all(newRSS) inside refreshRSS but it throws another error stating: is not iterable.
I'm not sure what exactly is firebase expectingas return parameter.
When you have to resolve promise in loop, try to use map. This will work:
const refreshRSS = functions.pubsub.schedule('every 30 mins').onRun(async context => {
let newRSS = await addRSS();
return await newPodcasts;
});
addRSS = async () => {
const newRSSFeed = new Array();
let rssURLs = await db.collection('rssURLs').get();
let promise = rssURLs.map(async rssURLsObject=>{
rss = rssURLsObject.data();
let rssData = await parser.parseURL(rss.url);
let con = await db.collection('rss').doc(encodeURIComponent(rss.url)
newRSSFeed.push(con)
.set(podcast));
})
await Promise.all(promise)
return newRSSFeeds;
}

Running 2 async methods in sequence. 2nd method dependent on 1st method

I'm new with Typescript/JS and I'm coming from Java.
I am trying to solve a scenario wherein the 2nd method call is dependent on 1st method's return value.
I managed to create a getCustomerNumber() method that is able to return a string value instead of undefined The 2nd method getCustomerName() needs the return value of getCustomerNumber() as argument for it to successfully return the customer name value.
I tried making both of them as async methods that returns a Promise<string> object. However, getCustomerName() fails to retrieve a customer number from getCustomerNumber()
private async getCustomerNumber(rorn : string): Promise<string> {
const getCustNo = new Request();
getCustNo.transaction = "GetCustNum";
getCustNo.argument = rorn;
let customerNo;
await Service.execute(getCustNo).then((response: IResponse) => {
customerNo = response.items[0]["CustNum"];
}).catch((response: IResponse) => {
this.log.Error(response.errorMessage);
});
return customerNo;
}
private async getCustomerName(cuno: string) : Promise<string> {
const getCustName = new Request();
getCustName.transaction = "GetCustName";
getCustName.argument = cuno;
let customerName;
await Service.execute(getCustName).then((response: IResponse) => {
customerName = response.items[0]["CustName"];
}).catch((response: IResponse) => {
this.log.Error(response.errorMessage);
});
return customerName;
}
private async callMethodsAndSetLabelText() : Promise <void> {
const orderNum = "123456";
const customerNumber = await this.getCustomerNumber(orderNum).trim());
const customerName = await this.getCustomerName(customerNumber) ; //get customer name by number
console.log(customerName);
const labelElement = new LabelElement();
labelElement.setText(customerName);
}
Calling the methods:
callMethodsAndSetLabelText()
Logging the customer name in callMethodsAndSetLabelText() results to undefined
console.log(customerName); //undefined
I don't know how to set it up in a way that will work for the 2nd method since it's dependent on the 1st method.
I'd appreciate any comments or suggestions.
Thank you

How to await a subscribe in angular

my question is very simple... Consider the following angular method:
fillOrderHeader() {
let rows3:Array<OrderHeader>= new Array<OrderHeader>();
this.orderService.getAllOrdersHeaders().subscribe(headerList=>{
headerList.forEach(headerDoc=>{
headerDoc.get().subscribe(header=>{
let headerMetadata:OrderHeader= new OrderHeader();
headerMetadata.clientName= header.data().clientName;
headerMetadata.agencyName=header.data().agencyName;
rows3.push(headerMetadata);
});
})
});
return rows3;
}
How to await to subscription before to return a row3 array?
Many thanks!
async/await can only work with Promises. So to make it work in your example, you'll have to change your Observable(s) into Promise(s).
Good thing though is, you can simply use the toPromise API on an Observable for doing that.
Another thing that you'll have to do is declare your fillOrderHeader function as async. To do that you'll have to await the calls to this.orderService.getAllOrdersHeaders().toPromise() and headerDoc.get().toPromise()
Try this:
async fillOrderHeader() {
let rows3: Array<OrderHeader> = new Array<OrderHeader>();
const headerList = await this.orderService.getAllOrdersHeaders().toPromise();
for(let headerDoc of headerList) {
const header = await headerDoc.get().toPromise();
let headerMetadata: OrderHeader = new OrderHeader();
headerMetadata.clientName = header.data().clientName;
headerMetadata.agencyName = header.data().agencyName;
rows3.push(headerMetadata);
}
return rows3;
}
try this:
async fillOrderHeader() {
let rows3: Array<OrderHeader>= new Array<OrderHeader>();
let headerList = await this.orderService.getAllOrdersHeaders().toPromise();
for(let headerDoc of headerList){
let header = await headerDoc.get().toPromise();
let headerMetadata: OrderHeader = new OrderHeader();
headerMetadata.clientName = header.data().clientName;
headerMetadata.agencyName = header.data().agencyName;
rows3.push(headerMetadata);
}
return rows3;
}

Batch get DocumentReferences?

I'm trying to improve a firestore get function, I have something like:
return admin.firestore().collection("submissions").get().then(
async (x) => {
var toRet: any = [];
for (var i = 0; i < 10; i++) {
try {
var hasMedia = x.docs[i].data()['mediaRef'];
if (hasMedia != null) {
var docData = (await x.docs[i].data()) as MediaSubmission;
let submission: MediaSubmission = new MediaSubmission();
submission.author = x.docs[i].data()['author'];
submission.description = x.docs[i].data()['description'];
var mediaRef = await admin.firestore().doc(docData.mediaRef).get();
submission.media = mediaRef.data() as MediaData;
toRet.push(submission);
}
}
catch (e) {
console.log("ERROR GETTIGN MEDIA: " + e);
}
}
return res.status(200).send(toRet);
});
The first get is fine but the performance is worst on the line:
var mediaRef = await admin.firestore().doc(docData.mediaRef).get();
I think this is because the call is not batched.
Would it be possible to do a batch get on an array of mediaRefs to improve performance?
Essentially I have a collection of documents which have foreign references stored by a string pointing to the path in a separate collection and getting those references has been proven to be slow.
What about this? I did some refactoring to use more await/async code, hopefully my comments are helpful.
The main idea is to use Promise.all and await all the mediaRefs retrieval
async function test(req, res) {
// get all docs
const { docs } = await admin
.firestore()
.collection('submissions')
.get();
// get data property only of docs with mediaRef
const datas = await Promise.all(
docs.map(doc => doc.data()).filter(data => data.mediaRef),
);
// get all media in one batch - this is the important change
const mediaRefs = await Promise.all(
datas.map(({ mediaRef }) =>
admin
.firestore()
.doc(mediaRef)
.get(),
),
);
// create return object
const toRet = datas.map((data: MediaSubmission, i) => {
const submission = new MediaSubmission();
submission.author = data.author;
submission.description = data.description;
submission.media = mediaRefs[i].data() as MediaData;
return submission;
});
return res.status(200).send(toRet);
}

Categories