Отслеживание, когда несколько обратных вызовов завершены в node.js с Mongoose и FeedParser - PullRequest
1 голос
/ 04 марта 2012

Я пишу пакетный процесс для чтения RSS-канала и сохранения содержимого в 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 Ответ

1 голос
/ 27 марта 2012

Я тоже столкнулся с этой проблемой. Я бы подумал, что предпочтительный способ справиться с этим будет с событиями, однако при просмотре исходного кода там, кажется, не было ничего, что держало бы некоторый тип подсчета операций. В итоге я подключил его к 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));
...