使用Mongoose和FeedParser跟踪在node.js中完成多个回调的时间

时间:2012-03-04 07:19:55

标签: node.js mongodb asynchronous mongoose feedparser

我正在编写批处理来读取RSS源,并通过Mongoose将内容存储在MongoDB中。我会运行脚本,它会处理内容很好......但脚本不会返回到控制台。我的假设是我的数据库连接仍处于打开状态,这就是我没有返回的原因。

我无法跟踪解析完成的时间,因为我仍然可能会进行一些mongoose保存操作。

所以,我写了一个函数来跟踪我的开放数据库连接,以及我的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 个答案:

答案 0 :(得分: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));