【问题标题】:Waiting for a forEach to finish before return from my promise / function在从我的承诺/功能返回之前等待 forEach 完成
【发布时间】:2021-09-16 01:34:34
【问题描述】:

我正在使用 Firebase Cloud Firestore,但是,我认为这可能更像是 JavaScript 异步与同步承诺返回问题。

我正在执行查询以从一个集合中获取 ID,然后循环该查询的结果以根据该 ID 从另一个集合中查找单个记录。

然后我想将找到的每条记录存储到一个数组中,然后返回整个数组。

results.length 始终为 0,因为 return results 在 forEach 完成之前触发。如果我从 forEach 内部打印 results.length 它有数据。

如何等到 forEach 完成后再从外部 Promise 和外部函数本身返回?

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

【问题讨论】:

  • 您可以使用Promise.all 将一组promise 合并为一个promise。

标签: javascript node.js firebase google-cloud-firestore


【解决方案1】:

这里的技巧是用承诺而不是结果填充results。然后,您可以在该承诺数组上调用 Promise.all() 并获得您想要的结果。当然,您无法在推送承诺之前检查 doc.exists 是否存在,因此您需要在 Promise.all() 解决后进行处理。例如:

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
}

【讨论】:

  • 我看到一个使用 async/await 的类似问题 (stackoverflow.com/questions/49594472/…) - 在这种情况下正确的语法是:return await Promise.all(results)?
  • @Bruce 试过了,但它确实有效,但在我的情况下,用户数据仍然是一个承诺
【解决方案2】:

如果您有多个来自循环的工作项要同时执行,您可以从所有工作项中收集所有承诺,然后使用Promise.all() 等待它们全部完成。可能解决方案的一般形式如下所示:

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

您可以将其调整为适合您自己特定形式的内容。

【讨论】:

  • 我尝试了许多类似的解决方案,这是一个有效的解决方案。谢谢!
【解决方案3】:

使用for of 代替forEach。像这样:

for (const item of array) {
  //do something
}
console.log("finished");

finished”将在循环结束后被记录。

【讨论】:

    猜你喜欢
    • 2021-03-08
    • 2018-08-15
    • 2016-05-08
    • 2015-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-13
    • 2019-05-31
    相关资源
    最近更新 更多