web-dev-qa-db-fra.com

Attendre la fin d'un forEach avant de revenir de ma promesse / fonction

J'utilise Firebase Cloud Firestore, cependant, je pense que cela peut être plus un problème de retour de promesse asynchrone vs synchrone JavaScript.

Je fais une requête pour obtenir les ID d'une collection, puis je passe en revue les résultats de cette requête pour rechercher des enregistrements individuels d'une autre collection en fonction de cet ID.

Ensuite, je veux stocker chaque enregistrement trouvé dans un tableau, puis renvoyer le tableau entier.

results.length est toujours 0 car return results se déclenche avant la fin de forEach. Si j'imprime results.length de l'intérieur du forEach, il contient des données.

Comment puis-je attendre que le forEach soit terminé avant de revenir de la promesse extérieure et de la fonction extérieure elle-même?

         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);
            });
          }
7
AdamG

L'astuce consiste à remplir results avec des promesses plutôt que le résultat. Vous pouvez ensuite appeler Promise.all() sur ce tableau de promesses et obtenir les résultats souhaités. Bien sûr, vous ne pouvez pas vérifier si doc.exists Avant de pousser la promesse, vous devrez donc y faire face une fois que Promise.all() sera résolu. Par exemple:

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
}
8
Mark Meyer

Si vous avez plusieurs éléments de travail à effectuer en même temps qui proviennent d'une boucle, vous pouvez collecter toutes les promesses de tous les éléments de travail et attendre qu'ils se terminent tous par Promise.all () . La forme générale d'une solution possible ressemble à ceci:

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
})

Vous pouvez l'adapter à quelque chose qui convient à votre propre forme spécifique.

2
Doug Stevenson