【发布时间】:2020-06-29 11:46:49
【问题描述】:
我想构建一个以 Firebase 作为后端的问答应用。Firestore 中有三个集合:questions、answers 和 repliesToAnswers。 question中的文档有一个字段content,answers中的文档有两个字段answerid和content,repliesToAnswers中的文档有三个字段answerId,questionId和content。。 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