NodeJS批量Mongo操作,等待callback

我正在尝试在mongo数据库上执行批处理操作。 这个想法是迭代每个用户,然后find正在学习相同课程的其他用户,或者去同一所大学,并存储关于这些匹配的信息。

一切都包含在这样一个循环内:

User.find({}, function(err, doc){ doc.forEach(function(candidate){ //other find operations in here ... } } 

其中“用户”是在网站上注册的用户的集合。 我遇到的问题是,forEach循环为每个用户调度所有的callback,而我想等待forEach循环内的所有callback完成,在移动到下一个文档之前。

我试过使用asynchronous,但我似乎无法弄清楚这一点。

我该如何处理每个用户?

你可以使用async ,例如async.eachSeries

 async.eachSeries(doc, function (candidate, cb) { //other find operations in here ... // and you call cb() once they're done (important!) // or call cb('some error') if it failed }, function (err) { if (err) { // this means that some cb() above was called with error } else { // here all candidates are processed successfully } }); 

请参阅: https : //caolan.github.io/async/docs.html#eachSeries

将函数推入数组并稍后调用其他方法的一种方法是使用非常适合循环asynchronous操作的observables。

 var candidatesOps = []; User.find({}, function(err, doc){ doc.forEach(function(candidate){ var func = function(candidate){ //other find operations in here }; candidatesOps.push(func); ... } } if(candidatesOps){ //call them candidatesOps[0]() } 

无需为每个用户分派所有callback,就可以轻松完成此任务,在集合操作中使用$lookuppipe道,在用户集合上创build"self-join" ,使用$matchpipe道将文档过滤到只返回与其他用户共享相同课程的用户。

例如,下面将返回所有正在学习同一课程的用户,因为有一个名为course的字段:

 User.aggregate([ { "$match": { "course": { "$exists": true } } }, { "$lookup": { "from": "users", "localField": "course", "foreignField": "course", "as": "users_courses" } }, { "$match": { "users_courses.1": { "$exists": true } } } ], callback); 

在mongo shell中testing

 db.test.insert([ { "name": "a", "course": "maths" }, { "name": "b", "course": "english" }, { "name": "c", "course": "maths" }, { "name": "d", "course": "science" }, { "name": "e", "course": "maths" }, { "name": "f", "course": "history" }, { "name": "g", "course": "history" } ]) 

运行聚合操作

 db.test.aggregate([ { "$match": { "course": { "$exists": true } } }, { "$lookup": { "from": "users", "localField": "course", "foreignField": "course", "as": "users_courses" } }, { "$match": { "users_courses.1": { "$exists": true } } } ]) 

示例输出

 /* 1 */ { "_id" : ObjectId("58948c0dd04f1bbdbf331ea7"), "name" : "a", "course" : "maths", "users_courses" : [ { "_id" : ObjectId("58948c0dd04f1bbdbf331ea7"), "name" : "a", "course" : "maths" }, { "_id" : ObjectId("58948c0dd04f1bbdbf331ea9"), "name" : "c", "course" : "maths" }, { "_id" : ObjectId("58948c0dd04f1bbdbf331eab"), "name" : "e", "course" : "maths" } ] } /* 2 */ { "_id" : ObjectId("58948c0dd04f1bbdbf331ea9"), "name" : "c", "course" : "maths", "users_courses" : [ { "_id" : ObjectId("58948c0dd04f1bbdbf331ea7"), "name" : "a", "course" : "maths" }, { "_id" : ObjectId("58948c0dd04f1bbdbf331ea9"), "name" : "c", "course" : "maths" }, { "_id" : ObjectId("58948c0dd04f1bbdbf331eab"), "name" : "e", "course" : "maths" } ] } /* 3 */ { "_id" : ObjectId("58948c0dd04f1bbdbf331eab"), "name" : "e", "course" : "maths", "users_courses" : [ { "_id" : ObjectId("58948c0dd04f1bbdbf331ea7"), "name" : "a", "course" : "maths" }, { "_id" : ObjectId("58948c0dd04f1bbdbf331ea9"), "name" : "c", "course" : "maths" }, { "_id" : ObjectId("58948c0dd04f1bbdbf331eab"), "name" : "e", "course" : "maths" } ] } /* 4 */ { "_id" : ObjectId("58948c0dd04f1bbdbf331eac"), "name" : "f", "course" : "history", "users_courses" : [ { "_id" : ObjectId("58948c0dd04f1bbdbf331eac"), "name" : "f", "course" : "history" }, { "_id" : ObjectId("58948c0dd04f1bbdbf331ead"), "name" : "g", "course" : "history" } ] } /* 5 */ { "_id" : ObjectId("58948c0dd04f1bbdbf331ead"), "name" : "g", "course" : "history", "users_courses" : [ { "_id" : ObjectId("58948c0dd04f1bbdbf331eac"), "name" : "f", "course" : "history" }, { "_id" : ObjectId("58948c0dd04f1bbdbf331ead"), "name" : "g", "course" : "history" } ] } 

所有asynchronousforEachcallback完成后callback

 User.find({}, function(err, doc){ //use promises instead of using async var requests = doc.reduce((promiseChain, item) => { return promiseChain.then(() => new Promise((resolve) => { setTimeout(function(){ //sample delay console.log(item); resolve(); //iterate for next item },1000); })); }, Promise.resolve()).then(function(){ // last execution console.log('finish all'); }); });