无法迭代数组并保存到mongoose。 callback问题?

我正在学习node,express,mongo,在这个过程中,javascript。 我试图得到一个使用rssparser的function,获取一个故事列表,并将其保存到一个mongo数据库与mongoose。

我已经掌握了RSS技术,我正在迭代这些故事,这是我遇到的问题。 我想1)检查故事不存在于数据库中,2)如果没有,保存它。 我想我正在处理callback的方式迷路了。 这是我目前的代码,有评论。

rssparser.parseURL(url, options, function(err,out){ // out.items is an array of the items pulled var items = out.items; var story; for (var i=0; i<items.length; i++){ //create a mongoose story story = new schemas.Stories({ title: items[i].title, url: items[i].url, summary: items[i].summary, published: items[i].published_at }); //TODO: for testing - these show up correctly. //If I pull 10 stories, I get 10 entries from here that match //So "story" is holding the current story console.log("items[i] is :" + items[i].title); console.log("story title is : " + story.title); // setup query to see if it's already in db var query = schemas.Stories.findOne({ "title" : story.title, "url" : story.url }); //execute the query query.exec( function(err, row){ if(err) console.log("error-query: " + err); console.log("row: "+ row); if(!row) { // not there, so save console.log('about to save story.title: ' + story.title); story.save(function (err){ console.log("error in save: " + err); }); } }); } }); 

当这个运行,我看到的是很多控制台输出:

这是开始显示所有的故事(许多省略):

 items[i] is :TSA Drops Plan to Let Passengers Carry Small Knives on Planes story title is : TSA Drops Plan to Let Passengers Carry Small Knives on Planes items[i] is :BUILDING COLLAPSE:1 Reportedly Dead, 13 Pulled From Philly Rubble story title is : BUILDING COLLAPSE:1 Reportedly Dead, 13 Pulled From Philly Rubble items[i] is :CONTROVERSIAL PAST: Obama's UN Nominee Once Likened US 'Sins' to Nazis' story title is : CONTROVERSIAL PAST: Obama's UN Nominee Once Likened US 'Sins' to Nazis' items[i] is :WRITING OUT WRIGHTS: Bill Gives First Powered Flight Nod to Whitehead story title is : WRITING OUT WRIGHTS: Bill Gives First Powered Flight Nod to Whitehead items[i] is :BREAKING NEWS: Rice Named to Top Security Post Despite Libya Fallout story title is : BREAKING NEWS: Rice Named to Top Security Post Despite Libya Fallout 

然后继续像(很多省略):

 row: null about to save story.title: Best Ribs in America row: null about to save story.title: Best Ribs in America row: null about to save story.title: Best Ribs in America row: null about to save story.title: Best Ribs in America row: null about to save story.title: Best Ribs in America row: null about to save story.title: Best Ribs in America row: { title: 'Best Ribs in America', url: 'http://www.foxnews.com/leisure/2013/06/05/10-best-ribs-in-america/', published: 1370463800000, _id: 51af9f881995d40425000023, __v: 0 } 

它重复了“即将储存”的标题(这是饲料中的最后一个故事),并且像最后一行所显示的那样将故事保存一次。

console.log输出显示了我刚刚把它,所有的故事标题输出在顶部,然后从底部的query.exec()调用内的所有东西。

任何帮助表示赞赏…

这个问题是在callback中引用的故事将被设置为for循环中迭代的最后一件事,一旦callback将被执行,因为所有执行的函数都引用了同一个实例variables。

解决这个问题的最简单的方法是简单地将每个东西都包含在一个函数中,这个函数可以用参数立即执行,如下所示:

 rssparser.parseURL(url, options, function(err,out){ // out.items is an array of the items pulled var items = out.items; for (var i=0; i<items.length; i++){ (function(item) { //create a mongoose story var story = new schemas.Stories({ title: item.title, url: item.url, summary: item.summary, published: item.published_at }); // setup query to see if it's already in db var query = schemas.Stories.findOne({ "title" : story.title, "url" : story.url }); //execute the query query.exec( function(err, row){ if(err) console.log("error-query: " + err); console.log("row: "+ row); if(!row) { // not there, so save console.log('about to save story.title: ' + story.title); story.save(function (err){ console.log("error in save: " + err); }); } }); })(items[i]); } }); 

我没有testing过这个,但是我相信你会发现它会解决你的问题

另一个更容易,更简洁,更好的方法是遍历数组上的forEach循环中的项目,如果你的平台支持这个(node.js) – 这个版本更加漂亮:

 rssparser.parseURL(url, options, function(err,out){ // out.items is an array of the items pulled out.items.forEach(function(item) { //create a mongoose story var story = new schemas.Stories({ title: item.title, url: item.url, summary: item.summary, published: item.published_at }); // setup query to see if it's already in db var query = schemas.Stories.findOne({ "title" : story.title, "url" : story.url }); //execute the query query.exec( function(err, row){ if(err) console.log("error-query: " + err); console.log("row: "+ row); if(!row) { // not there, so save console.log('about to save story.title: ' + story.title); story.save(function (err){ console.log("error in save: " + err); }); } }); }); }); 

好吧,节点是事件驱动的服务器,JavaScript也是事件驱动的,所以你可以asynchronous调用东西。

你需要使用一些asynchronous模式来做你想做的事情。

首先,如果你正在使用mongoose,你可以利用它的模式类来检查已经存在的项目,而无需再次查询数据库:

 var mongoose = require('mongoose'); var schema = new mongoose.Schema({ title: String, url: { type: String, unique: true }, summary: String, published: Date }) var model = mongoose.model('stories', schema) 

该url是唯一的,所以保存将导致重复的错误和mongoose不会保存查询。

现在遍历这些项目,并保存每一个我们需要某种模式,幸运的是,我们有它的asynchronous :

 var async = require('async'); rssparser.parseURL(url, options, function(err, out){ async.each(out.items, function(item, callback){ var m = new model({ title: item.title, url: item.url, summary: item.summary, published: item.published_at }) m.save(function(err, result){ callback(null) }); }, function(err){ //we complete the saving we can do stuff here }); } 

我们在并行模式下使用asynchronous,因为我们不在乎是否有重复。 你也可以用一个数组来追踪它,你可以把它推给err || 结果,所以你可以看到你保存了多less物品。