节点与响应异步

node asynchronous with response

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.jsonpreturn空contributionList。 我已经尝试使用异步 (https://github.com/caolan/async)。在发送 res.jsonp 之前填写 contributionList 的好习惯是什么?

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

将您的 res.jsonp() 移动到填充 contributionList 的回调代码的末尾,而不是在回调之外。

因为你似乎有多个 find() 内部循环等等,而且你不能保证回调的顺序 运行,你可以使用 async (如你所提到的)来创建一个工作流以确保它们全部完成,然后 运行 最终回调(由 async 执行)以调用 res.jsonp().

因为您的数据库查询是异步的(它们稍后完成)并且您的其余代码不会等待它们,所以您的两个 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 个),并且每次完成一个请求时,您都会启动下一个等待请求。 async 库可以为您完成这种类型的管理,或者您可以相当简单地构建自己的小请求队列,每次完成一个请求时,您发送另一个请求。