节点与响应异步

时间:2015-05-13 15:39:14

标签: node.js asynchronous mongoose

function getResultsForOneDev(devID, res) {

    var Contribution = require('../db/Contribution.js').model;
    var SurveyState = require('../db/SurveyState.js').model;
    var SurveyAnswer = require('../db/SurveyAnswer.js').model;

    var contributionList = {
        "dev": [ {
            "contribs" : [ {
                "surveyStates" : [ {
                    "surveyAnswers" : [ { } ]
                } ]
            } ]
        } ]
    };

    Contribution.find({dev:devID}).exec(function (error, contribs){
        // console.log("contribs:"+contribs);        

        contributionList = contribs;
        console.log("contribs length:"+contribs.length);

        for (var i = 0 ; i<contribs.length ; i++) {

            (function(oneContrib) {

                //console.log('contribs ID '+oneContrib._id);

                SurveyState.find({contrib:oneContrib._id}).exec(function (error, surveyStates){

                    // console.log("surveyStates:"+surveyStates);

                    oneContrib.surveyStates = surveyStates;
                    console.log("surveyStates length:"+surveyStates.length);

                    for (var j = 0 ; j<surveyStates.length ; j++) {

                        (function(oneSurveyState) {

                            SurveyAnswer.find({surveyState:oneSurveyState._id}).exec(function (error, surveyAnswers){

                                // console.log("surveyAnswers:"+surveyAnswers);

                                oneSurveyState.surveyAnswers = surveyAnswers;
                                console.log("surveyAnswers length:"+surveyAnswers.length);

                            });
                        })(surveyStates[j]);
                    }
                });
            })(contribs[i]);
        };

    });
    res.jsonp(contributionList);
}

这个程序没有按我的意愿运行,res.jsonp返回空的contribList。 我已经尝试使用异步(https://github.com/caolan/async)。在发送res.jsonp之前填充contributionList有什么好的实践?

2 个答案:

答案 0 :(得分:1)

.find()是异步的。它会在回调已将值填充到contributionList之前立即返回。

res.jsonp()移至填充contributionList而不是回调之外的回调代码的末尾。

由于您似乎有多个find()内部循环和诸如此类的东西,并且您无法保证回调将运行的顺序,您可以使用async(如您所述)创建工作流以确保它们全部完成,然后运行最终回调(由async执行)以调用res.jsonp()

答案 1 :(得分:0)

因为您的数据库查询是异步的(它们将在稍后完成)并且其余代码不等待它们,所以您的两个for循环将在实际的异步响应之前完成很长时间。因此,您必须实际跟踪(以某种方式)最后一次异步响应的时间,以及现在contributionList数据结构中的所有数据,以便您现在可以发送您的响应。

我的偏好是使用promises和Promise.all()在完成任意数量的异步操作时触发操作,但我不知道你用的数据库接口知道哪些是这是一个通用的方法,它只是简单地使用一个手动计数器来跟踪有多少异步操作仍然在飞行中,当计数器变为零时,你现在拥有所有数据,你可以发送响应。

此代码的添加内容是使用变量remaining的代码行。

function getResultsForOneDev(devID, res) {

    var Contribution = require('../db/Contribution.js').model;
    var SurveyState = require('../db/SurveyState.js').model;
    var SurveyAnswer = require('../db/SurveyAnswer.js').model;

    var contributionList = {
        "dev": [ {
            "contribs" : [ {
                "surveyStates" : [ {
                    "surveyAnswers" : [ { } ]
                } ]
            } ]
        } ]
    };

    Contribution.find({dev:devID}).exec(function (error, contribs){
        // console.log("contribs:"+contribs);        

        contributionList = contribs;
        console.log("contribs length:"+contribs.length);

        // keep track of how many async responses are left to be processed
        // in a variable at a higher scope
        var remaining = 0;

        for (var i = 0 ; i<contribs.length ; i++) {

            (function(oneContrib) {

                //console.log('contribs ID '+oneContrib._id);

                SurveyState.find({contrib:oneContrib._id}).exec(function (error, surveyStates){

                    // console.log("surveyStates:"+surveyStates);

                    oneContrib.surveyStates = surveyStates;
                    console.log("surveyStates length:"+surveyStates.length);

                    // add how many more responses are pending
                    remaining += surveyStates.length;

                    for (var j = 0 ; j<surveyStates.length ; j++) {

                        (function(oneSurveyState) {

                            SurveyAnswer.find({surveyState:oneSurveyState._id}).exec(function (error, surveyAnswers){

                                // console.log("surveyAnswers:"+surveyAnswers);

                                oneSurveyState.surveyAnswers = surveyAnswers;
                                console.log("surveyAnswers length:"+surveyAnswers.length);

                                // mark one more processed and see if all remaining ones are done
                                --remaining;
                                if (remaining === 0) {
                                    res.jsonp(contributionList);
                                }

                            });
                        })(surveyStates[j]);
                    }
                });
            })(contribs[i]);
        };

    });
}

P.S。您应该意识到,您在某种程度上同时使用了大量请求(所有尝试并行运行),并且稍后数据库将实际完成所有这些请求。根据数据库的结构及其有效处理大量请求或与使用数据库的其他用户共享负载的能力,这有时不是最佳做法。因此,有时最好一次发送少量请求(例如3-5),每次完成时,都会启动下一个等待请求。异步库可以为您执行这种类型的管理,或者您可以相当简单地构建自己的小队列请求,每次完成后,您都会发送另一个。