2

I am trying to loop and get different documents from firestore. The 'document ids' are provided by an array named 'cart' as you can see in the code below.

The programming logic which I have tried goes like this the while loop in every iteration gets document from firestore and in first 'then' section it saves the data which it just have got and in second 'then' it increments the 'i' and does the next cycle of loop.

The problem is while loop doesn't wait for that get request to finish. It just keeps looping and crashes.

The thing is even if I somehow manage to do the loop part correct. How would I manage the overall execution flow of program so that only after completing the loop part further code gets executed since the code below uses the cart array which loop part updates.

let i = 0
while (i < cart.length) {
    let element = cart[i]
    db.collection(`products`).doc(element.productID).get().then((doc1) => {
        element.mrp = doc1.data().mrp
        element.ourPrice = doc1.data().ourPrice
        return console.log('added price details')
    }).then(() => {
        i++;
        return console.log(i)
    }).catch((error) => {
        // Re-throwing the error as an HttpsError so that the client gets the error details.
        throw new functions.https.HttpsError('unknown', error.message, error);
    });
}
return db.collection(`Users`).doc(`${uid}`).update({
    orderHistory: admin.firestore.FieldValue.arrayUnion({
        cart,
        status: 'Placed',
        orderPlacedTimestamp: timestamp,
        outForDeliveryTimestamp: '',
        deliveredTimestamp: ''
    })
}).then(() => {
    console.log("Order Placed Successfully");
})

2 Answers 2

1

Your question is not about firebase, you're asking about looping asynchronously. You can see some promises examples here, and async/await here

You can use reduce on the promises. Note that all the promises are being created at the same time, but the call to the server is done one after the other.

cart.reduce(
  (promise, element) =>
    promise.then(() => {
      return db.collection(`products`)
        .doc(element.productID)
        .get()
        .then(doc1 => {
          element.mrp = doc1.data().mrp;
          element.ourPrice = doc1.data().ourPrice;
        });
    }),
  Promise.resolve()
);

If you can, use async/await instead. Here all the promises are being created one after the other.

async function fetchCart() {
  for (const element of cart) {
    const doc1 = await db.collection(`products`).doc(element.productID);
    element.mrp = doc1.data().mrp;
    element.ourPrice = doc1.data().ourPrice;
    console.log('added price details');
  }
}
Sign up to request clarification or add additional context in comments.

Comments

1

Each call to Cloud Firestore happens asynchronously. So your while loop fires off multiple such requests, but it doesn't wait for them to complete.

If you have code that needs all the results, you will need to uses Promises to ensure the flow. You're already using the promise in the while loop to get doc1.data().mrp. If cart is an array, you can do the following to gather all promises of when the data is loaded:

var promises = cart.map(function(element) {
   return db.collection(`products`).doc(element.productID).get().then((doc1) => {
        return doc1.data();
    });
});

Now you can wait for all data with:

Promise.all(promises).then(function(datas) {
  datas.forEach(function(data) {
    console.log(data.mrp, data.ourPrice);
  });
});

If you're on a modern environment, you can use async/await to abstract away the then:

datas = await Promise.all(promises);
datas.forEach(function(data) {
  console.log(data.mrp, data.ourPrice);
});

2 Comments

I think there is a little typo in the last part, it should be await not away.
Good catch @YoannHercouet 👍. Thanks, and I updated my answer. Now I'm just wondering what an away keyword would do. :) --- Going forward btw, you can suggest an edit to any post (question or answer) by clicking the little edit link under them.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.