对模糊标题道歉 - 我想不出一种简洁地总结这个问题的方法。
这是交易。我有一个Node控制器,它必须执行一些顺序数据库操作,如下所示:
0. Given a surveyId
1. Find all entries in the question table, where question.surveyId = surveyId.
2. For each row in the question table returned by the previous query:
a) Find all entries in the `answer` table, where answer.questionId = question.id
b) For each row in the answer table returned by the previous query:
(i) Find the count of all entries in the vote table where vote.answerId = answer.id
控制器需要返回一个对象,该对象包含每个answerId在投票表中有条目的次数。它看起来像{1:9, 2:21, 3:0}
,其中1,2,3是答案ID,9,21和0是投票表中具有该答案ID的行数。
我一直在使用Q库来避免真正深度嵌套的回调。我有一个runQuery实用程序方法,它返回一个promise并在数据库IO完成后解析它。
现在,我有一些看起来像:
runQuery("find questions with surveyid matching given surveyid")
.then({
"for each question row returned by query:"
runQuery("find answers with questionid matching this question row")
.then({
"for each answer row returned by query:"
runQuery("find votes with answerID matching this answer row")
.then({
"for each vote row"
"increment the count for this answerid in the return object"
})
})
})
问题是,当控制器返回时返回对象总是空的,因为没有足够的时间让所有的数据库操作完成并且答案要解决(至少,我认为这是问题 - 显然真的很难解决像这些异步承诺这样的事情。)
有更好的方法吗?我应该放弃Q并处理一堆嵌套回调吗?
答案 0 :(得分:5)
使用单个查询执行此操作是有好处的。客户端和服务器之间不会有“聊天”,在这种情况下,通过网络可以节省大约2次往返(通常每次十分之一秒,大约是闪烁所需的时间,也就是说,可感知的人类时间和漫长的无聊等待机器。)
但是,如果您需要按顺序编写承诺,有很多选项。这是与您的伪代码形式匹配的。把它分解成更小的函数会很好。
runQuery("find questions with surveyId matching given surveyId")
.then(function (questions) {
return Q.all(questions.map(function (question) {
return runQuery("find answers matching question.id")
.then(function (answers) {
question.answers = answers;
return Q.all(answers.map(function (answer) {
return runQuery("find votes for each matching question")
.then(function (votes) {
answer.votes = votes;
return answer;
})
}))
})
.thenResolve(question);
}))
});
这将产生一系列问题的承诺,用各自的答案数字注释,以及用他们的投票注释的答案。
答案 1 :(得分:3)
您不能对异步操作使用常规顺序编程。因此,您不能拥有下一行顺序代码可以使用的返回对象。
相反,必须从成功处理程序调用使用返回对象的代码,或者承诺完成最后一个操作。这就是异步编码的工作原理,必须采用其中一种技术才能使事情正常工作。
因此,在你的伪代码中,它看起来像这样(以**开头的行是我添加的行):
runQuery("find questions with surveyid matching given surveyid")
.then({
"for each question row returned by query:"
runQuery("find answers with questionid matching this question row")
.then({
"for each answer row returned by query:"
runQuery("find votes with answerID matching this answer row")
.then({
"for each vote row"
"increment the count for this answerid in the return object"
** call function and pass it the return object
to process the final result
})
})
})
// you cannot put code here that uses the return object because
// the return object is not yet available