“意外的令牌”在 Nodejs 中递归调用 async 函数时

发布于 2025-01-11 16:54:46 字数 1661 浏览 0 评论 0原文

我的应用包含 Firebase Firestore 中带有嵌套评论的帖子,其结构使得每个带有 docID 的帖子/评论都有一个子集合 postComments。因此,给定的帖子/评论可以有无限数量的嵌套评论。

comments
 - docID
   postComments
    - docID
    - docID
    - docID
 - docID
   postComments
    - docID
    - docID

我目前正在编写一个 Firebase 云函数来递归查询给定 docID 的所有文档和子集合文档,并以数组返回所有这些文档。我的计划是定义 getChildComments 异步函数,它接受 docID 并返回该文档的 postComments 子集合中的所有文档。然后我会递归调用 getChildComments 直到我构建了一个包含线程中所有嵌套注释的数组。

exports.loadWholeCommentThread = functions.https.onCall(async (data, context) => {
       let comments = await getChildComments(data.rootID);
       return comments;
});

async function getChildComments(docID) {
  try {
     const db = admin.firestore();
     const commentsRef = db.collection('comments').doc(docID).collection('postComments');
     var comments = [];
     const commentsQuerySnap = await commentsRef.get();

     commentsQuerySnap.forEach((comment) => {
       let commentData = comment.data();
       comments.push(commentData);
       if (commentData.commentCount > 0) {
         let childComments = await getChildComments(commentData.commentID);
         comments.concat(childComments);
       }
     });

     return comments;
  } catch (error) {
     functions.logger.log(error);
     throw new functions.https.HttpsError('unknown', 'ERROR0', { message: error.message } )
  }
}

不幸的是,当我尝试部署代码时,我收到错误 Parsing error。我在 getChildComments 内递归调用 getChildComments 的行上出现意外标记 getChildComments。从该行删除等待可以修复构建问题,但递归调用不会完成。

我应该如何解决我的问题?或者有更好的方法来查询所有嵌套文档吗?

My app contains posts with nested comments in Firebase Firestore structured such that each post/comment with docID has a sub collection postComments. Thus, a given post/comment can have an infinite number of nested comments.

comments
 - docID
   postComments
    - docID
    - docID
    - docID
 - docID
   postComments
    - docID
    - docID

I am currently writing a Firebase cloud function to recursively query all documents and sub collection documents of a given docID and return all of those documents in an array. My plan was to define the getChildComments async function which takes in a docID and returns all of the documents in that document's postComments sub collection. I would then recursively call getChildComments until I have built an array with all of the nested comments in a thread.

exports.loadWholeCommentThread = functions.https.onCall(async (data, context) => {
       let comments = await getChildComments(data.rootID);
       return comments;
});

async function getChildComments(docID) {
  try {
     const db = admin.firestore();
     const commentsRef = db.collection('comments').doc(docID).collection('postComments');
     var comments = [];
     const commentsQuerySnap = await commentsRef.get();

     commentsQuerySnap.forEach((comment) => {
       let commentData = comment.data();
       comments.push(commentData);
       if (commentData.commentCount > 0) {
         let childComments = await getChildComments(commentData.commentID);
         comments.concat(childComments);
       }
     });

     return comments;
  } catch (error) {
     functions.logger.log(error);
     throw new functions.https.HttpsError('unknown', 'ERROR0', { message: error.message } )
  }
}

Unfortunately, when I try to deploy my code, I get the error Parsing error. Unexpected token getChildComments on the line where I recursively call getChildComments inside of getChildComments. Removing the await from this line fixes the build issue but then the recursive call doesn't finish.

How should I fix my issue? Or is there a better way to query all nested documents?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

心是晴朗的。 2025-01-18 16:54:46

这是因为您在 async 函数之外使用了 await(请注意,它位于箭头函数内部!)。

const comments = [];
const commentsQuerySnap = await commentsRef.get();

commentsQuerySnap.forEach((comment) => {
  let commentData = comment.data();
  comments.push(commentData);
  if (commentData.commentCount > 0) {
    let childComments = await getChildComments(commentData.commentID); // the keyword "await" here is invalid
    comments = comments.concat(childComments);
  }
});

但是您不能只将 async 添加到此箭头函数,因为这样您的代码将无法正确等待 comments 数组被填充。

要正确解决此问题,除了使用 Promise.all 等待之外,您还需要在 commentsQuerySnap.docs 数组上使用 .map()要检索的每个评论(及其子评论)。

const comments = [];
const commentsQuerySnap = await commentsRef.get();

await Promise.all(
  commentsQuerySnap.docs.map(
    async (comment) => {
      let commentData = comment.data();
      comments.push(commentData);
      if (commentData.commentCount > 0) {
        let childComments = await getChildComments(commentData.commentID);
        comments = comments.concat(childComments);
      }
    })
  )
);

虽然上面的块有效,但注释数组可能不符合您的预期。如果您必须保持所获取注释的顺序,以便它们与查询的顺序相同,则应返回每个文档的构建注释数组,然后在检索到所有注释后将它们展平。

// removed const comments = []; here
const commentsQuerySnap = await commentsRef.get();

const arrayOfCommentThreads = await Promise.all(
  commentsQuerySnap.docs.map(
    async (comment) => {
      let commentData = comment.data();
      const commentThread = [commentData];
      if (commentData.commentCount > 0) {
        let childComments = await getChildComments(commentData.commentID);
        commentThread = commentThread.concat(childComments);
      }
      return commentThread;
    })
  )
);

const comments = arrayOfCommentThreads.flat();

就我个人而言,我更喜欢使用展开运算符,而不是使用 .concat ,如下所示:

const commentsQuerySnap = await commentsRef.get();

const arrayOfCommentThreads = await Promise.all(
  commentsQuerySnap.docs.map(
    async (comment) => {
      const commentData = comment.data();
      if (commentData.commentCount === 0) {
        return [commentData];
      }
      
      const childComments = await getChildComments(commentData.commentID);
      return [commentData, ...childComments];
    })
  )
);

const comments = arrayOfCommentThreads.flat();

This is because you have used await outside of an async function (note that it is inside an arrow function!).

const comments = [];
const commentsQuerySnap = await commentsRef.get();

commentsQuerySnap.forEach((comment) => {
  let commentData = comment.data();
  comments.push(commentData);
  if (commentData.commentCount > 0) {
    let childComments = await getChildComments(commentData.commentID); // the keyword "await" here is invalid
    comments = comments.concat(childComments);
  }
});

But you can't just add async to this arrow function, because then your code won't properly wait for the comments array to be filled.

To properly fix this, you need to use .map() on the commentsQuerySnap.docs array in addition to using Promise.all to wait for each comment (and its children) to be retrieved.

const comments = [];
const commentsQuerySnap = await commentsRef.get();

await Promise.all(
  commentsQuerySnap.docs.map(
    async (comment) => {
      let commentData = comment.data();
      comments.push(commentData);
      if (commentData.commentCount > 0) {
        let childComments = await getChildComments(commentData.commentID);
        comments = comments.concat(childComments);
      }
    })
  )
);

While that above block works, the comments array may be out of order to what you were expecting. If you must maintain order of the comments fetched so they are in the same order as the query, you should return the built comments array for each document and then flatten them after they all have been retrieved.

// removed const comments = []; here
const commentsQuerySnap = await commentsRef.get();

const arrayOfCommentThreads = await Promise.all(
  commentsQuerySnap.docs.map(
    async (comment) => {
      let commentData = comment.data();
      const commentThread = [commentData];
      if (commentData.commentCount > 0) {
        let childComments = await getChildComments(commentData.commentID);
        commentThread = commentThread.concat(childComments);
      }
      return commentThread;
    })
  )
);

const comments = arrayOfCommentThreads.flat();

Personally, I prefer the spread operator to using .concat like so:

const commentsQuerySnap = await commentsRef.get();

const arrayOfCommentThreads = await Promise.all(
  commentsQuerySnap.docs.map(
    async (comment) => {
      const commentData = comment.data();
      if (commentData.commentCount === 0) {
        return [commentData];
      }
      
      const childComments = await getChildComments(commentData.commentID);
      return [commentData, ...childComments];
    })
  )
);

const comments = arrayOfCommentThreads.flat();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文