【问题标题】:node asynchronous with response节点与响应异步
【发布时间】:2015-07-25 00:24:08
【问题描述】:
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 返回空的contributionList。 我已经尝试过异步(https://github.com/caolan/async)。在发送 res.jsonp 之前填写贡献列表的好做法是什么?

【问题讨论】:

  • 解决这个问题的最简单方法是使用异步。你能发布使用它的代码版本吗?

标签: node.js asynchronous mongoose


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

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

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

    此代码的添加是使用变量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]);
            };
    
        });
    }
    

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

    【讨论】:

    • 您好,感谢您的回复。我终于达到了这个目的:使用剩余的并运行。在调用 getResultsForOneDev 函数后,我忘记在其他函数中删除 res.end()。
    猜你喜欢
    • 2015-08-19
    • 1970-01-01
    • 2018-10-01
    • 2013-06-22
    • 2016-11-03
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    相关资源
    最近更新 更多