【问题标题】:Trouble reading data in Firebase Cloud Function在 Firebase Cloud Function 中读取数据时遇到问题
【发布时间】:2026-02-14 02:35:01
【问题描述】:

尝试从 users 集合中的给定用户读取 pushToken(在对另一个集合进行更新操作之后)返回 undefined

exports.addDenuncia = functions.firestore
  .document('Denuncias/{denunciaID}')
  .onWrite((snap, context) => {
    const doc = snap.after.data()
    const classificadoId = doc.cid
    const idTo = doc.peerId

    db.collection('Classificados').doc(classificadoId)
      .update({
        aprovado: false
      })
      .then(r => {
        getToken(idTo).then(token => {
          // sendMsg...
        })
      }).catch(updateErr => {
        console.log("updateErr: " + updateErr)
      })

    async function getToken(id) {
      let response = "getTokenResponse"
      console.log("id in getToken: " + id)
      return db.collection('users').doc(id).get()
        .then(user => {
          console.log("user in getToken: " + user.data())
          response = user.data().pushToken
        })
        .catch(e => {
          console.log("error get userToken: " + e)
          response = e
        });
      return response
    }

    return null
  });

这是来自 FB 控制台日志:

-1:43:33.906 AM 函数执行开始

-1:43:36.799 AM 函数执行耗时 2894 毫秒,完成状态为:'ok'

-1:43:43.797 AM getToken 中的 ID:Fm1RwJaVfmZoSgNEFHq4sbBgoEh1

-1:43:49.196 AM 用户在 getToken:未定义

-1:43:49.196 AM error get userToken: TypeError: Cannot read property 'pushToken' of undefined

-1:43:49.196 AM 返回令牌:未定义

我们可以从 db 的截图中看到该文档确实存在:

希望有人能指出我在这里做错了什么。

添加了 @Renaud 的第二个部署示例的屏幕截图:

【问题讨论】:

  • 这里的第一个问题是您根本没有正确处理承诺。当所有异步工作完成时,您不会从*函数返回承诺,并且您的 getToken 函数总是将返回“getTokenResponse”。 Cloud Functions 并不是一个了解 Promise 工作原理的好地方。我建议花一些时间在更简单的环境中了解 JavaScript Promise 以了解它们的工作原理。

标签: firebase google-cloud-firestore google-cloud-functions


【解决方案1】:

正如 Doug 在他的评论中所写,您需要“从*函数返回一个承诺,该承诺会在所有异步工作完成时解决”。他还在官方视频系列中很好地解释了这一点:https://firebase.google.com/docs/functions/video-series/(尤其是名为“Learn JavaScript Promises”的 3 个视频)。您绝对应该观看它们,强烈推荐!

因此,对您的代码进行以下修改应该可以工作(未经测试):

exports.addDenuncia = functions.firestore
    .document('Denuncias/{denunciaID}')
    .onWrite(async (snap, context) => {   // <- note the async keyword

        try {

            const doc = snap.after.data()
            const classificadoId = doc.cid
            const idTo = doc.peerId

            await db.collection('Classificados').doc(classificadoId)
                .update({
                    aprovado: false
                });

            const userToSnapshot = await db.collection('users').doc(idTo).get();
            const token = userToSnapshot.data().pushToken;

            await sendMsg(token);  // <- Here you should take extra care to correctly deal with the asynchronous character of the sendMsg operation

            return null;  // <-- This return is key, in order to indicate to the Cloud Function platform that all the asynchronous work is done

        } catch (error) {
            console.log(error);
            return null;
        }

    });

由于您在代码中使用了async 函数,因此我使用了async/await 语法,但我们可以通过使用then() 方法链接promise 来编写它,如下所示。

此外,在您的情况下,我不确定它是否会增加任何值以将获取令牌的代码放入函数中(除非您想从其他云函数调用它,但您应该将其移出addDenuncia云函数)。这就是为什么它被主 try 块中的两行代码所取代。

通过then() 方法链接承诺的版本

在这个版本中,我们将异步方法返回的不同承诺与then() 方法链接起来。与上面的async/await 版本相比,它非常清楚地表明了“从所有异步工作完成时解析的*函数返回一个promise”的含义

exports.addDenuncia = functions.firestore
    .document('Denuncias/{denunciaID}')
    .onWrite((snap, context) => {   // <- no more async keyword

        const doc = snap.after.data()
        const classificadoId = doc.cid
        const idTo = doc.peerId

        return db.collection('Classificados').doc(classificadoId)  // <- we return a promise from the top level function
            .update({
                aprovado: false
            })
            .then(() => {
                return db.collection('users').doc(idTo).get();
            })
            .then(userToSnapshot => {
                if {!userToSnapshot.exists) {
                   throw new Error('No document for the idTo user');
                }
                const token = userToSnapshot.data().pushToken;

                return sendMsg(token);   // Again, here we make the assumption that sendMsg is an asynchronous function
            })
            .catch(error => {
                console.log(error);
                return null;
            })

    });

【讨论】:

  • 感谢您的帮助。您的第一个示例给出: -Function execution started -TypeError: Cannot read property 'pushToken' of undefined at exports.addDenuncia.functions.firestore.document.onWrite (/srv/index.js:277:48) at at process ._tickDomainCallback (internal/process/next_tick.js:229:7) - 函数执行耗时 3006 毫秒,完成状态:'ok'
  • 你确定有对应用户的文档吗?你能在const token = ...之前加console.log(userToSnapshot.exists);吗?
  • 添加了这一行,现在返回:用户文档数据:未定义
  • 为了清楚起见,index.js 以 const functions = require('firebase-functions'); 开头const admin = require('firebase-admin'); admin.initializeApp(); const db = admin.firestore();
  • 好的,现在可以正常工作了。我最终使用了您的第一个示例,因为它更具可读性。将以这种方式完成我未来的所有云功能。