Javascript 对顺序/相关操作使用嵌套Q承诺

Javascript 对顺序/相关操作使用嵌套Q承诺,javascript,node.js,q,Javascript,Node.js,Q,为这个模糊的标题道歉——我想不出一个简单的方法来总结这个问题 事情是这样的。我有一个节点控制器,它必须执行一些顺序数据库操作,如下所示: 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

为这个模糊的标题道歉——我想不出一个简单的方法来总结这个问题

事情是这样的。我有一个节点控制器,它必须执行一些顺序数据库操作,如下所示:

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
控制器需要返回一个对象,该对象包含投票表中每个应答ID有一个条目的次数。它看起来像
{1:9,2:21,3:0}
,其中1,2,3是答案ID,9,21和0是投票表中带有该答案ID的行数计数

我一直在使用Q库来避免真正的深度嵌套回调。我有一个runQuery实用程序方法,它返回一个承诺,并在数据库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,处理一堆嵌套回调吗?

你不能用常规的顺序编程来处理异步操作。因此,您不能拥有下一行顺序代码可以使用的返回对象

相反,使用返回对象的代码必须从成功处理程序调用,或承诺完成最后一个操作。异步编码就是这样工作的,必须采用其中一种技术才能正常工作

因此,在伪代码中,它看起来是这样的(以**开头的行是我添加的):


使用单个查询进行此操作有其优点。客户机和服务器之间不会有“聊天”,在这种情况下,这将为您节省大约2次网络往返(通常每个往返时间为十分之一秒,约为眨眼所需的时间,也就是说,可感知的人类时间和对机器的长时间无聊等待)

然而,如果您曾经需要用Q顺序编写承诺,那么有很多选择。这与伪代码的形式相匹配。最好将其分解为更小的函数

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);
    }))
});
这将产生一系列问题的承诺,并用相应的答案数组进行注释,答案用投票进行注释


另请参见

您是否应该返回
运行查询
?如果您使用的是支持SQL的数据库,那么您的整个操作应该只使用一个SQL查询即可完成。如果没有,那么您需要重新编写代码,在整个操作完成后,需要异步触发此结果。异步是有传染性的——如果一个操作的一部分是异步的,那么所有的操作都必须是异步的。@MikeEdwards我喜欢这个想法——我甚至没有想到这一点。我对Javascript比对SQL更熟悉!谢谢。您要传递到
then
方法中的是什么?它应该是一个回调函数,但您的函数看起来像是一个对象或带有字符串注释的块。@Bergi-这只是我的quick n'dirty伪代码:)有意义。我想我应该用另一种方式来思考这个问题。直接调用另一个函数的问题是,这是一个HTTP控制器——从一个函数中最容易形成HTTP响应(使用DB i/o的结果)。我想我可以接受@Mike Edwards的建议,试着把它写成一个SQL查询)。@jfriend00“你不能用异步操作进行顺序编程”,我不同意。我以前做过这样的工作,其中一个异步操作依赖于另一个异步操作的结果/响应。它工作得很好。我的声明意味着你不能在异步操作中一个接一个地使用常规的顺序编程-你必须使用专门为异步操作设计的技术。异步库就是这样一种机制,也是我在回答中使用的承诺。
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);
    }))
});