2012-03-04 66 views
1

我正在编写一个批处理过程来读取RSS源,并通过Mongoose将内容存储在MongoDB中。我会运行脚本,它会处理内容,但是脚本不会返回到控制台。我的假设是我的数据库连接仍然打开,这就是为什么我没有返回。使用Mongoose和FeedParser在node.js中完成几个回调时的跟踪

我无法追踪解析完成时的情况,因为我仍然可能会执行一些猫鼬保存操作。

所以,我写了一个函数来跟踪我的开放数据库连接,以及我的RSS解析的状态。但是我的代码最终变得非常冗长。我想知道是否有更好的模型/模式来做这样的事情。

var FeedParser = require('feedparser') 
    , mongoose = require('mongoose'); 

var TEST_RSS_URL = "./test/data/20120303-seattle.rss"; 
var OPEN_DB_CONNECTIONS = 0; 
var PARSING_DONE = false; 

/* 
* Keeps track of open database connections, and closes the connection when done 
*/ 
function track_and_close_database(mode) { 
    switch(mode) 
    { 
    case 'open': 
     OPEN_DB_CONNECTIONS++; 
     break; 
    case 'close': 
     OPEN_DB_CONNECTIONS--; 
     if (0 == OPEN_DB_CONNECTIONS && PARSING_DONE) conn.close(); 
     break; 
    case 'parseStart': 
     PARSING_DONE = false; 
     break; 
    case 'parseEnd': 
     PARSING_DONE = true; 
     if (0 == OPEN_DB_CONNECTIONS && PARSING_DONE) conn.close(); 
     break; 
    } 
} 

function parse_stuff(stuff) { 
    // do some stuff 
    setTimeout(console.log("parsed some stuff",20)); 
} 

function main() { 
    parser = new FeedParser(); 

    parser.on('article', function(article) { 
     track_and_close_database('open'); 
     // check to see if we already have this listing 
     stuff_model = conn.model('stuff'); 
     stuff = stuff_model.findOne({'href': article.link}, function (error, doc) { 
      if (error) { 
       track_and_close_database('close'); 
       return; 
      } 
      // this one doesn't exist yet, parse and save 
      if (null == doc) { 
       listing = parse_stuff(article); 

       // if listing is valid, save it! 
       if (null != listing) { 
        listing.save(function (error) { track_and_close_database('close') }); 
       } 
       // parsing failed 
       else track_and_close_database('close'); 
      } 
      // nothing to do, already in the database 
      else track_and_close_database('close'); 
     }); 
    }); 

    // Completed parsing the RSS file 
    parser.on('end', function(article) { 
    track_and_close_database('parseEnd'); 
    }); 

    track_and_close_database('parseStart') 
    parser.parseFile(TEST_RSS_URL); 
} 

// run this thing 
main(); 

回答

1

我也遇到过这个问题。我认为处理这个问题的首选方法是使用事件,但是在查看源代码时,似乎没有任何内容保留某些类型的操作。我最终把它连接到EventEmitter上。更好的是,如果Mongoose在保存之前和之后发出事件,所以我不必将其插入到我的所有模型中。

这里是我是如何做的一个样本:

/* lib/render_log.js */ 
/* Model for managing RenderLog */ 

var common = require("./common"); 
common.initialize_locals(global); 

var mongoose = require("mongoose"), 
    Schema = mongoose.Schema; 

var RenderLogSchema = new Schema({ 
    renderer: String, 
    template: String, 
    content: {} 
}); 

RenderLogSchema.pre('save', function(next){ 
    this.db.model('RenderLog').emit('open_db_op', this); 
    next(); 
}); 

RenderLogSchema.post('save', function(){ 
    this.db.model('RenderLog').emit('close_db_op', this); 
}); 

mongoose.connect('mongodb://localhost/time-jr-development'); 

var RenderLog = mongoose.model("RenderLog", RenderLogSchema); 
exports = module.exports = RenderLog; 

其次是我的测试可执行文件:

/* bin/test_mongoose.js */ 

var async = require('async'); 
var RenderLog = require("../lib/render_log"); 

var ConnectionManager = { 
    open_db_ops: 0, 

    new_db_op: function(){ 
     this.open_db_ops ++; 
    }, 

    close_db_op: function(){ 
    this.open_db_ops --; 
    }, 

    close: function(self){ 
     if(!self) 
     self = this; 
    if(self.open_db_ops > 0){ 
     console.log("Waiting...") 
     process.nextTick(async.apply(self.close, self)); 
    }else{ 
     RenderLog.db.close(); 
    } 
    } 
}; 


RenderLog.on("open_db_op", function(model){ 
    ConnectionManager.new_db_op(); 
}); 

RenderLog.on("close_db_op", function(model){ 
    ConnectionManager.close_db_op(); 
}) 

new RenderLog({renderer: "foo", template: "foo.html", content: {"bar": 1}}).save(); 
new RenderLog({renderer: "foo", template: "foo.html", content: {"bar": 2}}).save(); 
new RenderLog({renderer: "foo", template: "foo.html", content: {"bar": 3}}).save(); 
new RenderLog({renderer: "foo", template: "foo.html", content: {"bar": 4}}).save(); 
new RenderLog({renderer: "foo", template: "foo.html", content: {"bar": 5}}).save(); 
// You have to push this to the next tick otherwise it gets called before the save 
// events have been emitted 
async.nextTick(async.apply(ConnectionManager.close, ConnectionManager));