Search code examples
javascriptfirebaseasynchronousgoogle-cloud-firestorees6-promise

How to avoid nested Promises to read from Firestore and wait for all resolve?


Problem

How to fix nested Promises and resolve all Promises?

I am reading from three firestore collections and do some calculation by resolved data.

Structure of firestore

/users/userId
/purchases/purchaseId/
  - userId: string
  - initialPrice: number
  - productId: string
/products/productId
  - itemId: string
  - currentPrice: number

Calculation I want to do

Calculate for each user
Σ(currentPrice[i] - initialPrice[i])
, where i is an item for each user.

Code that doesn't work and need fixed

db.collections('users').get().then(snap => {
  snap.forEach(user => {
    const userId = user.id
    let totalProfit = 0

    db.collections('purchases').where('userId', '===', userId).get().then(snap =>{
      snap.forEach(purchase => {
        const initialPrice = purchase.initialPrice
        const productId = purchase.productId

        db.collections('products').doc(productId).get().then(doc => {
          const currentPrice = doc.currentPrice
          const profit = currentPrice - initialPrice
          totalProfit += profit
        })
      })
    })
    // Please wait for all Promises resolved.
    console.log(userId, totalProfit)
  })
})

Solution

  • Collect the inner promises and call Promise.all on them. Wait for that to resolve before calling console.log:

    db.collections('users').get().then(snap => {
      snap.forEach(user => {
        const userId = user.id;
        let totalProfit = 0;
    
        db.collections('purchases').where('userId', '===', userId).get().then(snap =>{
          const promises = [];
          snap.forEach(purchase => {
            const initialPrice = purchase.initialPrice;
            const productId = purchase.productId;
    
            promises.push(db.collections('products').doc(productId).get().then(doc => {
              const currentPrice = doc.currentPrice;
              const profit = currentPrice - initialPrice;
              totalProfit += profit;
            }));
          });
          return Promise.all(promises);
        }).then(() => console.log(userId, totalProfit));
      });
    });