在Node.js服务器上的POST请求上执行循环

时间:2015-12-11 15:38:37

标签: node.js post asynchronous foreach callback

我试图计算客户端通过POST请求(在Node.js服务器上)传递的json列表中每个性别的人数。我在理解javascript异步,回调和闭包时遇到了问题。

我想要的是: 从客户端获取列表, 对于每个条目询问我的收藏是否是m,f或u, 计算有多少fs,ms和我们, 使用三个值将数组发送到客户端。

我总是得到"在发送标题后不能设置标题"或异步执行导致的类似错误。我尝试了不同的回调订单和许多不同的选项。

这就是服务器上的功能的样子:

app.post('/genderize', function(req, res){
        createCounter("conto", req, function(req,contat ){
            count(req, contat);
        }).then(res.send( result ));
    });

function createCounter( nome, req, callback ) {
        result = [0,0,0];
        var contatore = function(){
            var m = 0;
            var f = 0;
            var u = 0;

            addM = function(){ console.log( "m++ "+result[1]);result[1]++; };
            addF = function(){ f++; };
            addU = function(){ u++; };

            getM = function(){ return this.m;};

            getResult = function(){
                console.log( result+ " *  "+ getM() + "  *  " + this.u + "  *  "+ this.f );
                return result;
            };
            return {
                addM: addM,
                addF: addF,
                addU: addU,
                getResult: getResult
                }; 
        }
            callback( req, contatore() );
    }

function count( req, counter ){
        var collection = db.get('nomi');
        var data = req.body.data;
        data.forEach(function(value, i){
            collection.find({ nome : req.body.data[i].name.split(" ")[0].toUpperCase() }, { fields: {_id:0, nome:0}}, function (err, docs) {
                        if (!isEmptyObject(docs)) {
                            docs = JSON.parse(JSON.stringify(docs));;
                            if(docs[0].sesso == "M"){                   
                                counter.addM();
                            } else {
                                counter.addF();
                            }
                        } else {
                            counter.addU();
                        }
            });
        });
    }

2 个答案:

答案 0 :(得分:0)

这是一种更好的方法。这真的清理了javascript的异步性质。检查我在这里使用的异步库。

var collection = db.get('nomi');
var async = require('async');

app.post('/genderize', function(req, res){
    let countingObject = {
        females: 0,
        males: 0,
        unknown: 0
    };

    async.each(req.body.data, function(name, callback) {
        collection.findOne({ nome : name.split(" ")[0].toUpperCase() }, { fields: {_id:0, nome:0}}, function (err, nameObject) {
            //instead, maybe check if it is male, female, or otherwise mark as unknown?
            if (!isEmptyObject(nameObject)) {
                //this object probably has getters that you could use instead
                nameObject = JSON.parse(JSON.stringify(nameObject));
                if(nameObject.sesso == "M"){                   
                    countingObject.males++;
                } else {
                    countingObject.females++;
                }
            } else {
                countingObject.unknown++;
            }
            callback();
        });
    }, function() {
        res.setHeader('Content-Header', 'application/json');
        res.send(JSON.stringify(countingCallback));
    });
});

答案 1 :(得分:0)

此示例有几个问题,但您遗漏的主要问题是,当您执行数据库查询时,collection.find调用将立即返回,但只会执行其回调(function(err, docs) )在数据库回复之后的某个时间。

这是一个重写的工作:

app.post('/genderize', function(req, res) {
    if (!req.body.data || req.body.data.length === undefined) {
        return res.status(400).send('Invalid request body.');
    }
    countGenders(db.get('nomi'), req.body.data, function (err, genders) {
        if (err) return res.status(500).send('Unable to process request.');
        res.send([genders.M, genders.F, genders.U]);
    });
});

function getGenderFromName(collection, name, next) {
    collection.find({nome : name.split(" ")[0].toUpperCase()}, {fields: {_id:0, nome:0}}, function (err, docs) {
        if (err) return next(err);
        var gender = 'U';
        if (docs && docs.length > 0) {
            gender = (docs[0].sesso == "M") ? 'M' : 'F';
        }
        next(null, gender);       
    });
}

function countGenders(collection, data, next) {
    var result = { M: 0, F: 0, U: 0 };
    var series = function(i) {
        if (i == data.length) return next(null, result);
        getGenderFromName(collection, data[i].name, function(err, gender) {
            if (err) return next(err);
            result[gender]++;
            series(i+1);
        });
    };
    series(0);
}

让我们回顾一下这些变化:

  • 删除了createCounter结构。对于这个简单的例子,不需要沉重的,获取/设置模式。
  • 检查每个异步回调中的错误值

    if (err) return next(err);
    

    在路由处理程序中,通常您需要使用res.status(500).send()结束请求。在大多数其他情况下,return next(err)会冒泡'错误。

  • 将数据库查询移动到新函数getGenderFromName。它主要保留您的原始代码。这是可选的,但大大提高了count功能的可读性。
  • 最后,使用适当的异步迭代模式重写count函数,由http://book.mixu.net/node/ch7.html提供。 Mixu给异步节点提供了一个非常容易理解的解释,给它一个读数。

    更好的选择是使用优秀的async模块。您可以将count方法重写为

    function countGenders(collection, data, next) {
        var result = { M: 0, F: 0, U: 0 };
        async.eachSeries(
            data,
            function (value, next) {
                getGenderFromName(collection, value.name, function(err, gender) {
                    if (err) return next(err);
                    result[gender]++;
                    next();
                });
            },
            function (err) { next(err, results); }
        );
    }
    

    Async包含许多不同的控制流方法,而不仅仅是简单的迭代。