【问题标题】:Wait for firestore queries to finish before return a cloud function response在返回云函数响应之前等待 firestore 查询完成
【发布时间】:2019-06-25 09:28:36
【问题描述】:

我有一个云函数,可以在 3 个不同的集合中运行 3 个 firestore 查询。每个查询在 then 块内都有一个 foreach 循环,在 foreach 循环之后它在集合中执行更新。代码如下:

    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!");

那么如何在所有操作完成后返回响应呢?

【问题讨论】:

  • 你对someCollectionRefX.update(....)做了什么?? update() 不是 CollectionReference 的方法,而是 DocumentReferenceWriteBatch 的方法。
  • 对不起,我的意思是 docRef,我会改变它
  • 好的,让我根据您的更新更新我的答案。

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


【解决方案1】:

如果您在 Cloud Function 中调用一些异步 Firestore 方法(例如 get() 用于 CollectionReferenceupdate() 用于 DocumentReference),您只需链接这些方法返回的不同承诺。

因此,根据您的问题代码,您可以将其修改如下:

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

请注意,这将起作用,因为someCollectionRefssomeDocRefs 的数量是预先知道的。如果您要执行可变数量的异步操作,则需要使用 Promise.all() 方法,如其他答案中所建议的那样。


万一3块

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

可以完全分开执行(即每个块的结果不会影响其他块),您可以并行化调用如下:

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

【讨论】:

  • 这应该可以,但我认为它有一个缺点,它们不能并行运行。有没有办法同时开始所有查询,然后等待它们和它们的 then 块代码完成,以便我可以发送响应?
  • 是的,有一种方法 (Promise.all()) 但这完全取决于不同的调用如何相互依赖。 db.collection("someOtherCollection").get().then(results=> {... someDocRef.update(....); })这3个block可以独立执行吗?
  • 是的,它们可以独立执行。现在每个 Promise 都解析为一个 Promise,因为 someDocRef.update() 返回一个 Promise。所以我需要另一个承诺?
  • 正如developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 中所解释的,“Promise.all() 方法返回一个 Promise,当所有作为迭代传递的 Promise 都已解决时,该 Promise 将解决”。实际上 p1、p2 和 p3 返回一个 Promise,因为 then() 返回一个 Promise,而我们返回由 update() 返回的 Promise。所以你只需要用 p1、p2 和 p3 构建一个数组并将其传递给Promise.all(),如答案所示。
  • @GiannisSavvidis 你可以接受我的回答,如果它解决了你的问题。谢谢。
【解决方案2】:

如果你不想让你的函数成为异步函数,你可以这样做:

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!"));

【讨论】:

  • 是的,但这会在承诺解决后返回响应,对吧?每个 promise 的 then 块(我认为它在 promise 解决后运行)具有我想等待它们完成的 for 循环以及也是异步任务的更新操作。那么我应该如何使用 promise.all 呢?
  • 基本上它是冲洗和重复,如果您的then 中的代码是同步代码,那么您就完成了,如果不是,那么您可能需要返回该承诺(或承诺.all 以防你有多个异步调用),那么这可能会成为一个回调地狱,所以使用函数来封装常见任务 + async/await 应该有助于提高可读性
  • 我可以使云函数异步以便我可以使用 await 吗?
【解决方案3】:

也许你可以使用Promise.all 来链接承诺。 Promise.all MDN

来自 MDN 的 Promise 演示:

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"]

【讨论】:

    猜你喜欢
    • 2017-10-12
    • 2021-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多