Waiting for a forEach to finish before return from my promise / function

15,450

Solution 1

The trick here is to populate results with promises rather than the result. You can then call Promise.all() on that array of promises and get the results you want. Of course, you can't check if doc.exists before pushing the promise so you will need to deal with that once Promise.all() resolves. For example:

function getFacultyFavoritesFirebase() {
    var dbRef = db.collection("users").doc(global.user_id).collection("favorites");
    var dbQuery = dbRef.where("type", "==", "faculty");
    var dbPromise = dbQuery.get();
    // return the main promise
    return dbPromise.then(function(querySnapshot) {
        var results = [];
        querySnapshot.forEach(function(doc) {
            var docRef = db.collection("faculty").doc(doc.id);
            // push promise from get into results
            results.push(docRef.get())
        });
        // dbPromise.then() resolves to  a single promise that resolves 
        // once all results have resolved
        return Promise.all(results)
    })
    .catch(function(error) {
        console.log("Error getting documents: ", error);
    });
}

getFacultyFavoritesFirebase
.then(results => {
    // use results array here and check for .exists
}

Solution 2

If you have multiple items of work to perform at the same time that come from a loop, you can collect all the promises from all the items of work, and wait for them all to finish with Promise.all(). The general form of a possible solution looks like this:

const promises = []  // collect all promises here
items.forEach(item => {
    const promise = item.doWork()
    promises.push(promise)
})
Promise.all(promises).then(results => {
    // continue processing here
    // results[0] is the result of the first promise in the promises array
})

You can adapt this to something that suits your own specific form.

Share:
15,450

Related videos on Youtube

AdamG
Author by

AdamG

Updated on September 14, 2022

Comments

  • AdamG
    AdamG over 1 year

    I am using Firebase Cloud Firestore, however, I think this may be more of a JavaScript asynchronous vs synchronous promise return issue.

    I am doing a query to get IDs from one collection, then I am looping over the results of that query to lookup individual records from another collection based on that ID.

    Then I want to store each found record into an array and then return the entire array.

    results.length is always 0 because return results fires before the forEach completes. If I print results.length from inside the forEach it has data.

    How can I wait until the forEach is done before returning from the outer promise and the outer function itself?

             getFacultyFavoritesFirebase() {
                var dbRef = db.collection("users").doc(global.user_id).collection("favorites");
                var dbQuery = dbRef.where("type", "==", "faculty");
                var dbPromise = dbQuery.get();
                var results = [];
                return dbPromise.then(function(querySnapshot) {
                    querySnapshot.forEach(function(doc) {
                      var docRef = db.collection("faculty").doc(doc.id);
                      docRef.get().then(function(doc) {
                        if (doc.exists) {
                            results.push(doc);
                        }
                      })
                    });
                    console.log(results.length);
                    return results;
                })
                .catch(function(error) {
                    console.log("Error getting documents: ", error);
                });
              }
    
  • lbrndnr
    lbrndnr about 6 years
    I saw a similar question (stackoverflow.com/questions/49594472/…) that used async/await - would the correct syntax in that case be: return await Promise.all(results)?
  • user3417479
    user3417479 over 3 years
    I tried many similar solutions, this is the one that worked. thanks!