Mendi Sterenfeld
Mendi Sterenfeld

Reputation: 397

async function returns Promise <Pending>

i'm building a crud app for a project, people can add view and delete entries, i'm using nodejs, js and fireabse for this. i have such a firestore database structure:

entries:
   --entry id
      --entry data
users:
   --user id
      --user email (query)
      -- my entries (collection)
           -- entries id
              --entry id 

now i want to display all users entries, so i created this module.exports function:

module.exports = {  
  //get all the users entries from database with passed in uid ('/dashboard')
  getUserEntries: async uid => {
    //get all his entries docs id from "myEntries" collection in an, array
    const usersEntriesId = await db
      .collection("users")
      .doc(uid)
      .collection("myEntries")
      .get()
      .then(entries => {
        return entries.docs.map(entry => entry.data().entry);
      })
      .catch(err => console.log(err));
    console.log(usersEntriesId); //works fine, logs array with ids

    const userEntriesDocs = usersEntriesId.map(async id => {
      const entry = await db
        .collection("entries")
        .doc(id)
        .get()
        .then(entry => {
          return entry.data();
        });
      console.log("hello:", entry); //works fine returns logs entry data

      return entry;
    });

    console.log("hello1: ", userEntriesDocs); //doesnt work i get "hello1:  [ Promise { <pending> },
 // Promise { <pending> },
  //Promise { <pending> } ]"
//i got three entries, that's why i get 3 times "Promise { <pending> }"
  }
};

so how do i resolve that?

Thanks

Upvotes: 1

Views: 7864

Answers (1)

skyboyer
skyboyer

Reputation: 23705

well, async function returns Promise, it how they works under the hood. If there was no .map you could just await on that function or use it as arbitrary Promise with .then() and .catch. But since there is array of Promise you need Promise.all to wait until all are resolved.

const userEntriesDocs = await Promise.all(usersEntriesId.map(async id => {
....
);

Beware: unlike .allSettled, .all() will fail immediately if any of subsequent Promise fails. So if for any reason you want to have data from those requests that succeeded, you need more complex logic.

As an alternative you may go through loop manually:

const userEntriesDocs = [];
for(const docPromise of userEntriesId.map(.....)) {
  userEntriesDocs.push(await docPromise);
}

But to me await Promise.all[...] is more readable.

Also I highlight there is array of Promises(requests have already been sent). If you try sending requests inside the loop like

const userEntriesDocs = [];
for(const id of userEntriesId) {
  userEntriesDocs.push(await db
        .collection("entries")
        .doc(id)
        .get()
        .then(entry => {
          return entry.data();
         })
  );
}

you will find that requests are going strictly one-by-one, not in parallel. That will require much more time to process the list.

Upvotes: 4

Related Questions