Home > database >  How to get data in sequence order from nested forEach function or Loop
How to get data in sequence order from nested forEach function or Loop

Time:08-14

I have a firestore collection structure like this

Chat Collection

      "chats": {
        "xyz_doc_id_1": { msg: "one", sender_id: "xyz123", timestamp: "xyz" },     //Chat from Person A
        "xyz_doc_id_2": { msg: "two", sender_id: "xyz456", timestamp: "xyz" },     //Chat from Person B
        "xyz_doc_id_3": { msg: "three", sender_id: "xyz123", timestamp: "xyz" },   //Chat from Person A
        "xyz_doc_id_4": { msg: "four", sender_id: "xyz456", timestamp: "xyz" },    //Chat from Person B
      }

User Collection

      "users": {
        "xyz_user_1": { uid: "xyz123", name: "Person A" },
        "xyz_user_2": { uid: "xyz456", name: "Person B" },
      }

Now I have to store chats data like

    const chatData = [
      {msg: "one", sender_name: "Person A"},
      {msg: "two", sender_name: "Person B"},
      {msg: "three", sender_name: "Person A"},
      {msg: "four", sender_name: "Person B"},
    ]

But for this, I have to first fetch the data of the chat, from which I will get the user's ID for each document. Now then, based on every user's ID I have to fetch the user names.

For which I am using this kind of nested code.

    const asynFunction = async () => {
      const chatList = await db.collection("chat").orderBy("timestamp").get()
      chatList.forEach((chatDoc) => {
        const msg = chatDoc.data().msg // Chat Message
        const sender_id = chatData.data().sender_id // Sender ID

        //Till here I am getting data in sequence

        //Here I want each sender's name on the basis of SENDER ID
        db.collection("users").doc(sender_id).get().then((docForName) => {
          const senderName = docForName.data().name

          //Here I am storing msg and name
          setChatData((prev) => [...prev, {msg: msg, name:senderName}])
        })
      })
    }

Expected Output -

   msg: "one", name: "Person A",   //From Person A
   msg: "two", name: "Person B",   //From Person B
   msg: "three", name: "Person A", //From Person A
   msg: "four", name: "Person B",  //From Person B

And what I am getting -

   msg: "one", name: "Person A",   //From Person A
   msg: "three", name: "Person A", //From Person A
   msg: "two", name: "Person B",   //From Person B
   msg: "four", name: "Person B",  //From Person B

I've done that with chained conditional as well but result is same. How can this be done in sequence order?

CodePudding user response:

One way to do this is to use for of instead of forEach, so that the get calls get executed one by one:

const asynFunction = async () => {
  const chatList = await db.collection("chat").orderBy("timestamp").get()
  for (const chatDoc of chatList.docs) {
    const msg = chatDoc.data().msg // Chat Message
    const sender_id = chatData.data().sender_id // Sender ID

    const docForName = await db.collection("users").doc(sender_id).get();
    const senderName = docForName.data().name

    //Here I am storing msg and name
    setChatData((prev) => [...prev, {msg: msg, name:senderName}])
  })
}

Also see: Using async/await with a forEach loop


Another way would be to use Promise.all to wait for all promises to finish, and then process them in the same order:

const asynFunction = async () => {
  const chatList = await db.collection("chat").orderBy("timestamp").get()
  const promises = chatList.docs.map((chatDoc) => {
    const msg = chatDoc.data().msg // Chat Message
    const sender_id = chatData.data().sender_id // Sender ID

    return db.collection("users").doc(sender_id).get();
  });
  const results = Promise.all(promises);
  results.forEach((docForName) => {
    const senderName = docForName.data().name

    setChatData((prev) => [...prev, {msg: msg, name:senderName}])
  })
}
  • Related