【问题标题】:How to handle multiple dependent asynchronous queries in Firestore如何在 Firestore 中处理多个相关的异步查询
【发布时间】:2020-06-29 11:46:49
【问题描述】:

我想构建一个以 Firebase 作为后端的问答应用。Firestore 中有三个集合:questionsanswersrepliesToAnswersquestion中的文档有一个字段contentanswers中的文档有两个字段answeridcontentrepliesToAnswers中的文档有三个字段answerIdquestionIdcontent。。 p>

questions: {
    content
}


answers: {
    questionId
    content 

}

repliesToAnswers {
    content
    answerId
    questionId 
}

我的目标是构建一个 Restful API 端点 /question/:questionId 来获取这样的结构化数据

{
    "content": "How to ...",
    "answers": [
        {
            "content":"...",
            "replies": [
                {
                    "content":"..."
                },
                {
                    "content":"..."
                }
            ]
        },
        {
            "content":"...",
            "replies":[]
        }
    ],
}

所以我尝试编写容易出错的嵌套 Promise。


exports.getQuestion = (request, response) => {
  console.log(request.params);

  let questionId = request.params.questionId;
  let question = {};

  firebase
  .firestore()
  .collection('questions')
  .doc(questionId)
  .get()
  .then(doc => {
    if(!doc.exists) {
      throw Error("doc not exists")
    }

    return doc;
  })
  .then(doc => {
    question.username = doc.data().username
    return doc.id 
  })
  .then(id => {
    // retrive answsers 
    let answers = [];

    firebase.firestore()
    .collection('answers')
    .where('questionId', '==', id)
    .get()
    .then(snapshot => {
      if (snapshot.empty) {
        console.log('no answers');
        return answers;
      }
      

      snapshot.forEach(ans => {
        // retrive replies
       
        let replies = [];
        
        firebase.firestore()
          .collection('repliesToAnswers')
          .where('questionId','==',questionId)
          .where('answerId','==', ans.id)
          .get()
          .then(snapshot => {
            if(snapshot.empty) {
              console.log(`no reply to answer(id:${ans.id}) of question(${questionId})`);
              return [];
            }


            snapshot.forEach(reply => {
              console.log(reply.id);
              replies.push({
                content: reply.data().content
              })

            })

            return replies; 

           
          })

        answers.push({
          content: ans.data().content,
          replies: replies
        })
      
      });
      return answers;
    })
    .then(answers => {
      question.answers = answers;
      return response.json(question);
    })

  })
  .catch(error => {
    console.log(error);
    response.status(500).json({
      error: error.code
    })
  })
  
};

问题在于该函数为每个答案返回了空的回复数组。它跳过了检索每个答案的回复的请求。任何人都可以帮助我吗?还是有更好的风格来实现它?

-----更新-----

为了更容易阅读,我使用Promise.all() 保持相同的逻辑


exports.getQuestion = (request, response) => {


  let questionId = request.params.questionId;
  let question = {};
  let answers = [];
  
  // retrive attribute content for Object question
  let fetchQuestion = firebase.firestore()
                      .doc(`questions/${questionId}`)
                      .get()
                      .then(doc => {
                        if(!doc.exists) {
                          throw Error('doc not exists')
                        }
                        return doc 
                      })
                      .then(doc => {
                        question.content = doc.data().content
                      })
                      .catch(error => {
                        console.log(error)
                      });
  // push result to answers
  let fetchAnswers = firebase.firestore()
                      .collection('answers')
                      .where('questionId','==',questionId)
                      .get()
                      .then(snapshot => {
                        if(snapshot.empty) {
                          console.log('no replies'); 
                          return;
                        } else {
                          snapshot.forEach(ans => {
                            answers.push({
                              content: ans.data().content, 
                              replies: [], 
                              id: ans.id 
                            })
                          })
                        }
                      }).catch(error => {
                        console.log(error); 
                      });
  let fetchAnsReplies = fetchAnswers.then(() => {
    answers.forEach(ans => {
      firebase.firestore() 
      .collection('repliesToAnswers')
      .where('answerId','==',ans.id)
      .get()
      .then(snapshot => {
        if(snapshot.empty) {

          console.log('no reply');
          return;
        } else {

          
          
          snapshot.forEach(reply => {
             ans.replies.push({
               content: reply.data()
             })
           })
        } 
      }).catch(error => {
        console.log(error); 
      })
    })

  }).catch(error => {
    console.log(error);
  })

  return Promise.all([fetchQuestion,fetchAnswers, fetchAnsReplies])
  .then(() => {
    return response.json({...question, answers: answers})
  }).catch(error => {
    console.log(error);
    response.status(500).json({
      error: error.code
    })

  })

}

【问题讨论】:

  • 您在将push异步 插入之前使用replies 数组。您还需要等待快照循环中的承诺。
  • 如果将查询作为函数提取出来,代码会更容易阅读,例如getQuestion()getAnswers()getReplies(),每个都包含return firebase.firestore().method(...).method(...).get()。除此之外,整体方法基本上是合理的。只需要按照@Bergi 的指示进行修复。
  • 应该可以将数据传递到 Promise 链中并在最终的 .then() 中组合 question,从而避免需要相当丑陋的外部 let question = {}
  • @Roamer-1888 谢谢,终于通过Promise.all()解决了
  • @Bergi 谢谢,Promise.all() 解决了它,代码更优雅

标签: javascript firebase asynchronous google-cloud-firestore promise


【解决方案1】:

我终于通过Promise.all() 用更优雅的代码解决了它。

exports.getQuestion = (request, response) => {


  let questionId = request.params.questionId;
 // let question = {};
  //let answers = [];

  // get info except answers 
  // { content:XXX, answers: []  }
  let fetchQuestion = firebase.firestore()
                      .doc(`questions/${questionId}`)
                      .get()
                      .then(doc => {
                        if(!doc.exists) {
                          throw Error('doc not exists');
                        }
                        return doc 
                      })
                      .then(doc => {
                        return {
                          content: doc.data().content,
                          id: doc.id,
                          answers: []
                        }
                      })
                      .catch(error => {
                        console.log(error)
                      });

  // return answers 
  let fetchAnswers =  firebase.firestore()
                      .collection('answers')
                      .where('questionId','==',questionId)
                      .get()
                      .then(snapshot => {
                        if(snapshot.empty) {
                          console.log('no replies'); 
                          return [];
                        } else {
                          let answers = []; 
                          snapshot.forEach(ans => {
                            // add answers to question
                            answers.push({
                              content: ans.data().content, 
                              replies: [], 
                              id: ans.id 
                            })

                          })

                          return answers;
                        }
                      }).catch(error => {
                        console.log(error); 
                      });

  

  let fetchAnsReplies = fetchAnswers.then(answers => {
    var promises = []; 
   
    answers.forEach(ans => {
      var promise = firebase.firestore() 
      .collection('repliesToAnswers')
      .where('answerId','==',ans.id)
      .get()
      .then(snapshot => {
        if(snapshot.empty) {

          console.log('no reply');
          return;
        } else {
          
          snapshot.forEach(reply => {
         
            ans.replies.push({
              content: reply.data()
            })
          })
        } 
      }).catch(error => {
        console.log(error); 
      })

      promises.push(promise);
    })


    return Promise.all(promises).then(() => {
      
      return answers;
    })

  }).catch(error => {
    console.log(error);
  })

  return Promise.all([fetchQuestion,fetchAnswers, fetchAnsReplies])
  .then(results => {
    return response.json({...results[0],answers:results[2]});
  }).catch(error => {
    console.log(error);
    response.status(500).json({
      error: error.code
    })

  })

}


【讨论】:

    猜你喜欢
    • 2023-03-29
    • 1970-01-01
    • 2017-06-19
    • 1970-01-01
    • 2016-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-14
    相关资源
    最近更新 更多