Wait for firestore queries to finish before return a cloud function response

Viewed 3349

I have a cloud function that runs 3 firestore queries in 3 different collections. Each query has a foreach loop inside the then block and after the foreach loop it performs an update in a collection. Here is the code:

    const someDocRef
    const someDocRef2
    const someDocRef3

    db.collection("someOtherCollection").get().then(results=> {
       results.forEach(result=> {
          //do some work
       })
       someDocRef.update(....);
    })

    db.collection("someOtherCollection2").get().then(results=> {
       results.forEach(result=> {
          //do some work
       })
       someDocRef2.update(....);
    })

    db.collection("someOtherCollection3").get().then(results=> {
       results.forEach(result=> {
          //do some work
       })
       someDocRef3.update(....);
    })

    res.status(200).send("I waited for all the Queries AND the update operations inside the then blocks of queries to finish!");

So how can I return the response after all the operations finish?

3 Answers

If you call some asynchronous Firestore methods in your Cloud Function (like get() for a CollectionReference or update() for a DocumentReference) you just have to chain the different promises returned by those methods.

So, based on the code of your question you could modify it as follows:

    const someDocRef
    const someDocRef2
    const someDocRef3

    db.collection("someOtherCollection").get()
    .then(results => {
       results.forEach(result=> {
          //do some work
       })
       return someDocRef.update(....);
    })
    .then(() => {    
       return db.collection("someOtherCollection2").get();
    })
    .then(results => {
       results.forEach(result=> {
          //do some work
       })
       return someDocRef2.update(....);
    })
    .then(() => {    
       return db.collection("someOtherCollection3").get();
    })
    .then(results => {
       results.forEach(result=> {
          //do some work
       })
       return someDocRef3.update(....);
    })
    .then(() => {    
       res.status(200).send("I waited for all the Queries AND the update operations inside the then blocks of queries to finish!");
    })    

Note that this will work because the number of someCollectionRefs and someDocRefs is known upfront. In case you have a variable number of asynchronous operations to execute, you will need to use the Promise.all() method, as suggested in the other answers.


In case the 3 blocks

    db.collection("someOtherCollectionX").get()
    .then(results => {
       results.forEach(result=> {
          //do some work
       })
       return someDocRefX.update(....);
    })

can be executed totally separately (i.e. each block results does not impact the other blocks), you can parallelize the calls as follows:

    const someDocRef
    const someDocRef2
    const someDocRef3

    const p1 = db.collection("someOtherCollection").get()
    .then(results => {
       results.forEach(result=> {
          //do some work
       })
       return someDocRef.update(....);
    });

    const p2 = db.collection("someOtherCollection2").get()
    .then(results => {
       results.forEach(result=> {
          //do some work
       })
       return someDocRef2.update(....);
    });

    const p3 = db.collection("someOtherCollection3").get()
    .then(results => {
       results.forEach(result=> {
          //do some work
       })
       return someDocRef3.update(....);
    });

    Promise.all([p1, p2, p3])
    .then(() => {
        res.status(200).send("I waited for all the Queries AND the update operations inside the then blocks of queries to finish!")
    });

Maybe you could use Promise.all to chain the promises. Promise.all MDN

Promise Demo from MDN:

var promise1 = Promise.resolve(3);
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then(function(values) {
  console.log(values);
});
// expected output: Array [3, 42, "foo"]

If you do not want to make your function an async function you could do something like this:

const someCollectionRef
const someCollectionRef2
const someCollectionRef3

const promise1 = db.collection("someOtherCollection").get().then(results=> {
   results.forEach(result=> {
      //do some work
   })
   someCollectionRef.update(....);
})

const promise2 = db.collection("someOtherCollection2").get().then(results=> {
   results.forEach(result=> {
      //do some work
   })
   someCollectionRef2.update(....);
})

const promise3 = db.collection("someOtherCollection3").get().then(results=> {
   results.forEach(result=> {
      //do some work
   })
   someCollectionRef3.update(....);
})
Promise.all([promise1, promise2, promise3])
  .then(_ => res.status(200).send("I waited for all the Queries AND the update operations inside the then blocks of queries to finish!"));
Related