'Unexpected token' 在 Nodejs 中递归调用异步函数时

'Unexpected token' when recursively calling async function in Nodejs

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

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

我目前正在编写一个 Firebase 云函数来递归查询给定 docID 和 return 数组中所有这些文档的所有文档和子集合文档。我的计划是定义 getChildComments 异步函数,该函数接收 docID 和 return 文档的 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 } )
  }
}

不幸的是,当我尝试部署我的代码时,我在 getChildComments 中递归调用 getChildComments 的行中收到错误 Parsing error. Unexpected token getChildComments。从此行中删除 await 修复了构建问题,但递归调用没有完成。

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

这是因为您在 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 数组被填充。

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

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

虽然上面的块有效,但评论数组可能与您预期的顺序不一致。如果您必须维护获取的评论的顺序,以便它们与查询的顺序相同,您应该 return 为每个文档构建评论数组,然后在它们全部被检索后展平它们。

// 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();