【问题标题】:How to query firebase and output asynchronous results?如何查询firebase并输出异步结果?
【发布时间】:2020-09-11 16:09:29
【问题描述】:

今天一直在到处寻找,我的脑袋已经到了我真的不知道该怎么办了。

我正在尝试做一些对我来说听起来很简单的事情。然而,异步工作流让我崩溃了。

  1. 从 Firestore 中的 crawlJobs 中查找每个 ID
  2. 在另一个集合中获取这些 ID 的所有文档
  3. 将输出添加到数组中
  4. 返回数组的输出

因为一切都是异步的,所以我被困在数字 2 和 3 上,因为数字 4 的执行速度比异步代码的执行速度要快。

我的大问题,我该如何处理?

这是我的全部功能:

exports.publicOutput = functions
  .region("us-central1")
  .runWith(runtimeOpts)
  .https.onRequest(async (req, res) => {
    const projectAlias = req.query.projectalias;
    const apiKey = req.query.apikey;
    let status = 404;
    let data = {
      Message:
        "Unauthorized access! Please provide the correct authentication data.",
    };
    let response = data;
    let scrapeStorage = [];

    // check if credentials are provided
    if (!projectAlias || !apiKey) {
      return res.status(status).send(response);
    }

    // when both items provided execute this
    if (projectAlias && apiKey) {
      const snapshot = await db
        .collection("AdaProjects")
        .where("projectAlias", "==", projectAlias)
        .where("hasAccess", "array-contains", apiKey)
        .limit(1)
        .get();

      if (snapshot.empty) {
        return res.status(status).send(response);
      }

      if (!snapshot.empty) {
        snapshot.forEach((doc) => {
          projectData = doc.data();
        });

        status = 200;
      }

      const crawlJobIDs = projectData.crawlJobs;

      let scrapeIDs = []; 

      crawlJobIDs.forEach(async (jobID) => {
        const snapshot = await db
          .collection("scrapes")
          .where("crawlJobID", "==", jobID)
          .get();

        if (snapshot.empty) {
          console.log("not found jobID", jobID);
          return;
        }

        snapshot.forEach((doc) => {
          scrapeIDs.push(doc.id);
          console.log(scrapeIDs); // here everything is fine. But this outputs (logically) after "DONE"
        });

      });
      
      response = scrapeIDs;
    }

    console.log("DONE");
    return res.status(status).send(response);
  });

我还尝试将所有内容都放在一个函数中,并在函数的端点之前等待它。

 async function getAllScrapeIDs(crawlJobIDs) {
      let someData = [];
      try {
        crawlJobIDs.forEach(async (jobID) => {
          const snapshot = await db
            .collection("scrapes")
            .where("crawlJobID", "==", jobID)
            .get();

          if (snapshot.empty) {
            console.log("not found jobID", jobID);
            return;
          }

          snapshot.forEach((doc) => {
            someData.push(doc.id);
          });
        });
      } catch (error) {
        console.log(error);
        return null;
      }

      return someData;
    }

// and then later in the code 
const crawlJobIDs = projectData.crawlJobs;
response = await getAllScrapeIDs(crawlJobIDs);

响应仍然为空,因为异步代码仍未更新。

我还尝试在没有 async/await 的情况下编写所有内容,并应用了 .then.catch 选项。相同的输出。我的函数在用我要输出的数据填充数组之前完成。

我觉得这部分 const crawlJobIDs = projectData.crawlJobs; 确实有效。也许是因为它只搜索一项?

【问题讨论】:

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


    【解决方案1】:

    在 forEach 循环中使用 async/await 不是一个好主意,请参阅 hereUsing async/await with a forEach loop

    您应该使用Promise.all(),如下所示:

        const crawlJobIDs = projectData.crawlJobs;
    
        let scrapeIDs = [];
    
        let promises = [];
    
        crawlJobIDs.forEach((jobID) => {
            promises.push(db
                .collection("scrapes")
                .where("crawlJobID", "==", jobID)
                .get());
        });
    
        const snapshot = await Promise.all(promises);  
    
        // snapshot is an Array of QuerySnapshots
        // You need to loop on this Array
    
        snapshot.forEach(querySnapshot => {
            querySnapshot.forEach(documentSnapshot => {
                scrapeIDs.push(documentSnapshot.id);
            });
        });
    

    我让你管理快照为空的情况...


    另外,你可以使用另一种方法

      if (!snapshot.empty) {
        snapshot.forEach((doc) => {
          projectData = doc.data();
        });
    
        status = 200;
      }
    

    既然您知道querySnapshot 中最多有一个文档(因为.limit(1)),请执行以下操作:

      if (!snapshot.empty) {
        const doc = snapshot.docs[0];
        projectData = doc.data();
      
        status = 200;
      }
    

    【讨论】:

    • 这很棒@renaud。它立即奏效了。最后一个注释也很好地减少了一些代码行。也感谢您的参考。我将在 forEach 循环中创建不使用异步的心理节点。不过我想知道,这里发生了什么crawlJobIDs.forEach((jobID) => { promises.push( db.collection("scrapes").where("crawlJobID", "==", jobID).get() ); }); - 是不是 icw const snapshotGroup = await Promise.all(promises);.it 等待所有这些都完成,然后再继续并使用 snapshotGroup 循环它们?
    • 是的,“Promise.all() 方法将一个可迭代的 Promise 作为输入,并返回一个解析为输入 Promise 结果数组的 Promise。”见developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
    • 一分钟前读完那个。我将继续阅读有关此内容及其用例的更多信息。再次,非常感谢您的快速回复并在开始之前节省了我的周末:)
    • @arnoud,已经这样做了!实际上立即这样做了。也将其标记为答案。也许需要时间?
    猜你喜欢
    • 2020-06-01
    • 2011-08-14
    • 2014-07-20
    • 1970-01-01
    • 2017-09-09
    • 2020-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多