简体   繁体   English

使用嵌套的Q promises进行顺序/相关操作

[英]Using nested Q promises for sequential/dependent operations

Apologies for the vague title - I can't think of a way to sum this problem up succinctly. 为模糊的标题道歉 - 我想不出一种简洁地总结这个问题的方法。

Here's the deal. 这是交易。 I have a Node controller that has to do some sequential database operations, going something like this: 我有一个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

The controller needs to return an object containing the count of the number of times there is an entry in the vote table for each answerId. 控制器需要返回一个对象,该对象包含每个answerId在投票表中有条目的次数。 It would look something like {1:9, 2:21, 3:0} , where 1, 2, 3 are answer IDs and 9, 21, and 0 are counts of the number of rows with that answer ID in the vote table. 它看起来像{1:9, 2:21, 3:0} ,其中1,2,3是答案ID,9,21和0是投票表中具有该答案ID的行数的计数。

I've been using the Q library to avoid really deeply nested callbacks. 我一直在使用Q库来避免真正深度嵌套的回调。 I have a runQuery utility method that returns a promise and resolves it when the database IO is finished. 我有一个runQuery实用程序方法,它返回一个promise并在数据库IO完成后解析它。

Right now, I have something that looks like: 现在,我有一些看起来像:

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

The problem is, the return object is always empty when the controller returns, because there hasn't been enough time for all the database ops to finish and the promises to resolve (at least, I think that's the issue - it's obviously really difficult to troubleshoot things like these asynchronous promises.) 问题是,当控制器返回时返回对象总是空的,因为没有足够的时间让所有数据库操作完成并且承诺要解决(至少,我认为这是问题 - 显然很难解决这些异步承诺之类的问题。)

Is there a better way of doing this? 有没有更好的方法呢? Should I just give up on Q and deal with a bunch of nested callbacks? 我应该放弃Q并处理一堆嵌套回调吗?

There are merits to doing this with a single query. 使用单个查询执行此操作是有好处的。 There would be no “chat” between the client and server, which will save you about 2 round trips across the network in this case (typically a tenth of a second each, about the time it takes to blink, which is to say, a perceptible amount of human time and a long boring wait for a machine). 客户端和服务器之间不会有“聊天”,在这种情况下,通过网络可以节省大约2次往返(通常每次十分之一秒,大约是闪烁所需的时间,也就是说,可感知的人类时间和漫长的无聊等待机器)。

However, should you ever need to compose promises sequentially with Q, there are many options. 但是,如果您需要使用Q顺序编写承诺,则有许多选项。 This is one that matches the form of your pseudocode. 这是与您的伪代码形式匹配的。 It would be good to break it into smaller functions. 把它分解成更小的函数会很好。

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

This would produce a promise for an array of questions, annotated with its respective array of answers, and the answers annotated with their votes. 这将产生一系列问题的承诺,用各自的答案数组注释,以及用他们的投票注释的答案。

See also https://github.com/kriskowal/q#sequences 另见https://github.com/kriskowal/q#sequences

You can't use regular sequential programming with asynchronous operations. 您不能对异步操作使用常规顺序编程。 Thus, you can't have a return object that the next line of sequential code can use. 因此,您不能拥有下一行顺序代码可以使用的返回对象。

Instead, the code that uses the return object must be called from the success handler or promise completion of the last operation. 相反,必须从成功处理程序调用使用返回对象的代码,或者承诺完成最后一个操作。 This is how asynchronous coding works and one of these types of techniques must be adopted for things to work properly. 这就是异步编码的工作原理,必须采用其中一种技术才能使事情正常工作。

Thus, in your pseudo-code, it would look like this (the line starting with ** is what I added): 因此,在您的伪代码中,它看起来像这样(以**开头的行是我添加的):

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM