我试图计算客户端通过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();
}
});
});
}
答案 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包含许多不同的控制流方法,而不仅仅是简单的迭代。