【问题标题】:Confused on how to properly use promises in http Firebase Cloud Function对如何在 http Firebase Cloud Function 中正确使用 Promise 感到困惑
【发布时间】:2021-06-28 00:36:32
【问题描述】:

我不熟悉通过 http 触发函数为 Firebase 使用云函数,我对如何正确终止该函数感到困惑。我不确定我是否应该使用 res.sendStatus、返回一个 Promise 或两者兼而有之。

我的函数的目标是遍历集合“社区”中的多个文档。每个社区都有一个文档集合,我在其中查询具有最高值'hotScore'的文档。然后,我将包含该文档的 iOS 推送通知发送到某个主题(给定社区中的所有用户)。

不幸的是,我在运行代码时遇到了几个错误,例如Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the clientUnhandled rejection。我很可能这是由于我在处理函数终止时的疏忽,尽管到目前为止我一直对我所看到的在线资源感到困惑。有人介意看看我的代码/指出我正确的方向吗?非常感谢!

exports.sendNotificationTrendingPost = functions.https.onRequest(async (req, res) => {
  //Get communities collection from Firestore
  return admin.firestore().collection('communities').get().then((communities) => {
    var communityPromises = [];
    //Loop through each community
    communities.forEach((community) => {
      let communityID = community.get('communityID');
      let communityName = community.get('name');
      //Get the post with the highest hotScore
      let communityPromise = admin.firestore().collection('communities').doc(communityID).collection('posts').orderBy('hotScore', 'desc').limit(1).get().then((posts) => {
        let hottestPost = posts[0];
        let postID = hottestPost.get('postID');
        let postText = hottestPost.get('text');
        let currentDate = Date.now() / 1000;
        var message;
        //Verify that the hottest post was posted in the past 24 hours
        if (hottestPost.get('date') > (currentDate - 86400)) {
          //Build the notification text (shortening if too long)
          let shortenedPostText = postText.substring(0,60);
          var textEnd = '';
          if (postText.length > 60) {
            textEnd = '...';
          }
          let notificationText = 'Trending post on ' + communityName + ': ' + shortenedPostText + textEnd;
          //Build the push notification
          message = {
            apns: {
              headers: {
                'apns-push-type': 'alert'
              },
              payload: {
                aps: {
                  alert: {
                    body: notificationText,
                  },
                },
                postID: postID,
              },
            },
            topic: communityID
          }
        }
        //Send the message and return the promise
        if (message === null) {
          return null;
        } else {
          return admin.messaging().send(message);
        }
      })
      .catch(error => {
        console.log(error);
        res.status(500).send(error);
      })
      if (communityPromise !== null) {
        communityPromises.push(communityPromise);
      }
    })
    res.sendStatus(200);
    return Promise.all(communityPromises);
  })
  .catch(error => {
    console.log(error);
    res.status(500).send(error);
  })
})

【问题讨论】:

  • 我稍后会回到这个,但应该注意的是,为 HTTP 请求函数返回 Promise 没有任何作用。乍一看,您拨打res.status(500).send(error),然后拨打res.sendStatus(200)。我会考虑重构您的代码以在此处使用async/await 语法,因为您已经进入了嵌套承诺领域,这会让您发现这样的事情。

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


【解决方案1】:

正如 samthecodingman 所建议的,在您的情况下使用 async/await 会更好,因为它会简化代码并使其更易于阅读。

以下更改应该可以解决问题(未经测试)。请注意我们如何使用一组社区名称来将名称从一个循环传递到另一个循环。之所以可行,是因为对于 Promise.all(),返回值按 Promises 传递的顺序排列,而与完成顺序无关。

exports.sendNotificationTrendingPost = functions.https.onRequest(async (req, res) => {

    try {

        const db = admin.firestore();

        const communitiesQuerySnap = await db.collection('communities').get();

        const communityPromises = [];
        const communityNames = [];

        communitiesQuerySnap.forEach((community) => {
            let communityID = community.get('communityID');
            let communityName = community.get('name');
            communityNames.push(communityName);
            communityPromises.push(db.collection('communities').doc(communityID).collection('posts').orderBy('hotScore', 'desc').limit(1).get())
        });

        const postsQuerySnapArray = await Promise.all(communityPromises);

        const messagePromises = [];
        postsQuerySnapArray.forEach((postsQuerySnap, index) => {
            const hottestPost = postsQuerySnap.docs[0];
            const postID = hottestPost.get('postID');
            const postText = hottestPost.get('text');
            const currentDate = Date.now() / 1000;
            let message;

            if (hottestPost.get('date') > (currentDate - 86400)) {
                //Build the notification text (shortening if too long)
                let shortenedPostText = postText.substring(0, 60);
                var textEnd = '';
                if (postText.length > 60) {
                    textEnd = '...';
                }

                const communityName = communityNames[index];  // The two Arrays postsQuerySnapArray and communityName have the same order, because Promise.all keeps the order.

                let notificationText = 'Trending post on ' + communityName + ': ' + shortenedPostText + textEnd;
                //Build the push notification
                message = {
                    apns: {
                        headers: {
                            'apns-push-type': 'alert'
                        },
                        payload: {
                            aps: {
                                alert: {
                                    body: notificationText,
                                },
                            },
                            postID: postID,
                        },
                    },
                    topic: communityID
                }
                messagePromises.push(admin.messaging().send(message));
            }
        })

        await Promise.all(messagePromises);
        res.status(200).send({ result: "completed" }); // Or res.end()

    } catch (error) {
        console.log(error);
        res.status(500).send(error);
    }

});

【讨论】:

  • 这完全有道理——我现在对 Promise 和 async/await 有了更好的理解。非常感谢您的时间和回答! :)
  • 还有一个问题 - 当我在 postQuerySnap 中的一个元素上调用 get 时,我收到错误“TypeError: Cannot read property 'get' of undefined”。就我而言,当我调用 hottestPost.get('postID') 或 hottestPost.get('text') 时会引发错误。你知道是什么导致了这个问题吗?
  • 可能是因为一个或多个社区没有热分文档。您应该测试 postQuerySnap 是否为空
  • 嗯,我验证了所有社区都包含一个“帖子”集合,并且每个帖子文档都有一个 hotScore 属性。问题似乎是 postsQuerySnapArray 中的每个 postQuerySnap 都是未定义类型(甚至不是我可以测试它是否为空的数组)。
  • 嗯,这很有道理——完全不用担心!非常感谢您的帮助:)
猜你喜欢
  • 1970-01-01
  • 2015-11-29
  • 2021-03-05
  • 2011-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多