简体   繁体   English

等待云Function完成

[英]Wait for Cloud Function to be finished

Is there any way to wait for a Cloud Function, that was triggered by a Firestore document write, to finish?有没有办法等待由 Firestore 文档写入触发的 Cloud Function 完成?

Context:语境:

My app has groups.我的应用有群组。 Owners can invite other users to a group via an invite code.所有者可以通过邀请码邀请其他用户加入群组。 Users can write themselves as member of a group if they have the right invite code.如果用户有正确的邀请码,他们可以将自己写成一个组的成员。 They do this by writing the groups/{groupId}/members/{userId} document that contains their profile info.他们通过编写包含他们的个人资料信息的groups/{groupId}/members/{userId}文档来做到这一点。

To make reading more efficient, this info is copied to array members in the groups/{groupId} document by a Cloud Function.为了提高阅读效率,此信息由 Cloud Function 复制到groups/{groupId}文档中的数组members

The Cloud Function that does that is triggered by the document write.执行此操作的 Cloud Function 由文档写入触发。 It is usually finished after a couple of seconds, but there's no predictable execution time and it might take a bit longer if it is a cold start.它通常在几秒钟后完成,但没有可预测的执行时间,如果是冷启动,可能需要更长的时间。

After the user has joined the group, I forward them to the groups view in my app which reads the group document.用户加入群组后,我将他们转发到我的应用程序中读取群组文档的群组视图。 In order for the view to render correctly, the membership info needs to be available.为了正确呈现视图,成员信息需要可用。 So I would like to forward AFTER the Cloud Function has finished.所以我想在云 Function 完成后转发。

I found no way to track the execution of a Cloud Function that was triggered by a Firestore document write.我发现无法跟踪由 Firestore 文档写入触发的 Cloud Function 的执行。

A fellow developer recommended to just poll the groups/{groupId} document until the info is written and then proceed but this doesn't seem like a clean solution to me.一位开发人员建议只轮询groups/{groupId}文档,直到信息被写入,然后继续,但这对我来说似乎不是一个干净的解决方案。

Any ideas how this could be done better?有什么想法可以做得更好吗?

Is it possible to get a promise that resolves after the Cloud Function has finished?是否有可能在云 Function 完成后得到一个 promise 解析? Is there a way to combine a Firestore document write and a Cloud Function execution into one transaction?有没有办法将 Firestore 文档写入和 Cloud Function 执行合并到一个事务中?

Thanks for the hints, I came up with the following ways to deal with the problem.感谢您的提示,我想出了以下方法来处理这个问题。 The approach depends on if/when the user is allowed to read a document:该方法取决于是否/何时允许用户阅读文档:

A) User is member and leaves the group > at the start of the transaction they are allowed to read the group > the moment they can't read anymore confirms that the membership was successfully revoked: A)用户是成员并离开组 > 在事务开始时他们被允许阅读组 > 他们不能再阅读的那一刻确认成员资格被成功撤销:

async function leaveGroup (groupId) {
  await deleteDoc(doc(db, 'groups', groupId, 'members', auth.currentUser.uid))

  // Cloud Function removes the membership info
  // from the group doc...

  await new Promise((resolve) => {
    const unsubscribeFromSnapshot = onSnapshot(
      doc(db, 'groups', groupId),
      () => { }, // success callback
      () => { // error callback
        // membership info is not in the group anymore
        //   > user can't read the doc anymore
        //     > transaction was successful
        // read access was revoked > transaction was successful:
        unsubscribeFromSnapshot()
        resolve()
      }
    )
  })
}

B) User is not a member and wants to join the group > at the start of the transaction they are allowed to read the group > the moment they can read the group confirms that the membership was successfully confirmed (this is a simplified version that does not check the invite code): B) 用户不是成员并且想加入组 > 在交易开始时他们被允许阅读组 > 他们可以阅读组的那一刻确认成员资格已成功确认(这是一个简化版本不检查邀请码):

async function joinGroup (groupId) {
  try {
    await setDoc(
      doc(db, 'groups', groupId, 'members', auth.currentUser.uid),
      {
        userId: auth.currentUser.uid,
        userDisplayName: auth.currentUser.displayName
      }
    )

    // Cloud Function adds the membership
    // information to the group doc ...

    await new Promise((resolve) => {
      let maxRetries = 10
      const interval = setInterval(async () => {
      try {
        const docSnap = await getDoc(doc(db, 'groups', groupId))
        if (docSnap.data().members.includes(auth.currentUser.uid)) {
          // membership info is in the group doc
          //   > transaction was successful
          clearInterval(interval)
          resolve()                
        }
      } catch (error) {
        if (maxRetries < 1) {
          clearInterval(interval)
        }
      }
      maxRetries--
    }, 2000)
  })
}

Note: I went with polling here, but similar to what @samthecodingman suggested, another solution could be that the Cloud Function confirms the membership by writing back to the members document (which the user can always read) and you listen to snapshot changes on this document.注意:我在这里进行了轮询,但与@samthecodingman 所建议的类似,另一种解决方案可能是云 Function 通过写回members文档(用户始终可以读取)来确认成员资格,并且您可以在此收听快照更改文档。

C) Most straightforward way: someone else (the group owner) removes a member from the group > they have read access through the whole transaction > directly listen to snapshot changes: C) 最直接的方式:其他人(群组所有者)从群组中删除一个成员 > 他们在整个事务中具有读取权限 > 直接监听快照更改:

async function endMembership (groupId, userId) {

  await deleteDoc(doc(db, 'groups', groupId, 'members', userId))
    
  // Cloud Function removes the membership info
  // from the group doc...
    
  await new Promise((resolve) => {
    const unsubscribe = onSnapshot(doc(db, 'groups', groupId), (doc) => {
      if (!doc.data().members.includes(userId)) {
        // membership info is not in the group doc anymore
        //   > transaction was successful
        unsubscribe()
        resolve()
      }
    })
  })
}

In any case you should do proper error handling that covers other causes.在任何情况下,您都应该进行适当的错误处理以涵盖其他原因。 I left them out to demonstrate how to use the error handlers when waiting for gaining/loosing read access.我将它们排除在外是为了演示在等待获得/失去读取访问权限时如何使用错误处理程序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM