Jason
Jason

Reputation: 1141

Use Promise or Await in firebase query

I am trying to run a query that grabs a collection of locations. It then needs to check each location against a temp collection to make sure it does not already exist. If it doesn't exists it goes into the array. Next it loops through the array and randomly grabs three.

Here is my code:

let locations = []
        let getLocations = db.collection('locations')
            .where('area', '==', 'central')
            .get().then(snapshot => {
                snapshot.forEach(doc => {
                    let loc = doc.data()
                    loc.id = doc.id

                    console.log('1')

                    let locationsplayedRef = db.collection('locationsplayed')
                    locationsplayedRef = locationsplayedRef.where('location_id', '==', loc.id)
                    locationsplayedRef.get().then(snapshot => {
                        // make sure the location is not already in the db
                        if (snapshot.empty) {
                            // only grab activated locations
                            if(!loc.deactivated)
                                locations.push(loc)                    
                        }

                        console.log('2 ', locations)
                    })

                })

            }).then(() => {
                for (let i = locations.length - 1; i > 0; i--) {
                    const j = Math.floor(Math.random() * (i + 1));
                    [locations[i], locations[j]] = [locations[j], locations[i]];
                }

                let third = locations[0];
                let [first, second] = locations.filter(l => !l.restaurant).slice(1);
                let selectedLocations = [first, second, third];

                console.log('3 ', selectedLocations)
            })

The problem with this current code is the console log will look like this 1 3 2 and I need 1 2 3. I know I need to use a promise or await but I am still new to using these and am not sure how to implement it.

Could someone show me how to use either a Promise or Await so my code runs in the proper order?

Upvotes: 0

Views: 61

Answers (2)

danh
danh

Reputation: 62686

Two ideas will be useful: (1) factoring, so you can see what's going on, develop smaller, testable functions. (2) Promise.all(), to perform every promise generated in a loop.

// return a promise that resolves to the passed loc or null if the loc is played
function isLocationPlayed(loc) {
  let locationsplayedRef = db.collection('locationsplayed')
  locationsplayedRef = locationsplayedRef.where('location_id', '==', loc.id)
  return locationsplayedRef.get().then(snapshot => {
    // make sure the location is not already in the db
    return (snapshot.empty && !loc.deactivated) ? loc : null
  })
}

// return a promise that resolves to an array of locations that have been played
playedLocations() {
  let locations = []
  let getLocations = db.collection('locations').where('area', '==', 'central')
  return getLocations.get().then(snapshot => {
    // build an array of promises to check isLocationPlayed
    let promises = snapshot.docs.map(doc => {
      let loc = doc.data()
      return isLocationPlayed(loc)
    })
    // resolve when all of the passed promises are resolved
    return Promise.all(promises)
  }).then(results => {
    return results.filter(e => e) // remove nulls
  })
}

playedLocations().then(locations => {
  console.log(fyShuffle(locations))
})

// shuffle an array using Fisher Yates
function fyShuffle(a) {
  for (let i = a.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [a[i], a[j]] = [a[j], a[i]];
  }
  return a;
}

Upvotes: 3

Philip
Philip

Reputation: 871

I'm unable to test the following code, But could you try the following.

Take note of async and await

  async someFunction() {
    const locations = [];

    await db
      .collection("locations")
      .where("area", "==", "central")
      .get()
      .then(async snapshot => {
        const docs = snapshot.docs;
        // Must use for of loop here
        for (const doc of docs) {
          const loc = doc.data();

          await db
            .collection("locationsplayed")
            .where("location_id", "==", loc.id)
            .get()
            .then(snapshot => {
              // make sure the location is not already in the db
              if (snapshot.empty) {
                // only grab activated locations
                if (!loc.deactivated) {
                  locations.push(loc);
                }
              }
            });
        }
      });

    for (let i = locations.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [locations[i], locations[j]] = [locations[j], locations[i]];
    }

    let third = locations[0];
    let [first, second] = locations.filter(l => !l.restaurant).slice(1);
    let selectedLocations = [first, second, third];
  }

Upvotes: 1

Related Questions