3

I have a module that starts some background jobs in NodeJS (the jobs are HTTP requests), here is a simplified version of it:

var
util    = require('util'),
EE  = require('events').EventEmitter,
Winston = require('winston');
var logger = new Winston.Logger({transports: [new Winston.transports.Console()]});

function Bot() {
    EE.call(this);
    this.model = ['job1','job2','job3'];
    // null = never had a job, false = job started, true = job finished
    this.jobs = {
        'job1': null,
        'job2': null,
        'job3': null };
    var mirror = this;

    function start_job(job) {
        var t = Math.floor(Math.random() * 10 + 1) * 1000;
        logger.info('calling ' + job + ' for t=' + t);
        setTimeout(function() {
            logger.info('finished ' + job);
            mirror.jobs[job] = true;
        }, t);
    }

    this.on('start', function() {
        logger.info('Starting');
        while (mirror.isRunning()) {
            for (var i=0; i<mirror.model.length; i++) {
                var job = mirror.model[i];
                var oldJob = mirror.jobs[job];
                if (oldJob === null || oldJob === true) {
                    mirror.jobs[job] = false;
                    start_job(job);
                }
            }
            // mirror.shutdown();
        }
        logger.info('Shutting down');
    });
}

util.inherits(Bot, EE);

Bot.prototype.shutdown = function() {
    this.running = false;
};
Bot.prototype.start = function() {
    this.running = true;
    this.emit('start');
};
Bot.prototype.isRunning = function() {
    return this.running;
};

var bot = new Bot();
bot.start();

The problem is that the jobs never actually run. start_job() is called but the anon function in setTimeout() never fires. Here is the output:

info: Starting
info: calling job1 for t=6000
info: calling job2 for t=5000
info: calling job3 for t=9000

The script hangs up at this point, in an infinite loop.

Now, if I stop after the first loop iteration, by uncommenting mirror.shutdown(), the jobs start as expected:

info: Starting
info: calling job1 for t=6000
info: calling job2 for t=9000
info: calling job3 for t=1000
info: Shutting down
info: finished job3
info: finished job1
info: finished job2

But that is no good. I have no idea why this is happening, but I'm new to Node and still struggling with some concepts.

I know of a module called Background.js, but I don't know if this would be applicable here or how stable to is (doesn't seem to be an active project). I could try it but I still want to understand this problem before moving on.

4

1 に答える 1

1

問題は、それnode.jsがシングルスレッドであることです。その while ループは実行を継続し、イベント ループを占有するため、 は起動setTimeoutする機会がありません。ジョブを開始する while ループの代わりに、それを a setInterval(または再帰的setTimeout) に配置する必要があります。例えば:

this.on('start', function() {
    logger.info('Starting');
    for (var i=0; i<mirror.model.length; i++) {
        var job = mirror.model[i];
        var oldJob = mirror.jobs[job];
        if (oldJob === null || oldJob === true) {
            mirror.jobs[job] = false;
            start_job(job);
        }
    }
    setTimeout(function(){mirror.start()}, 50);
    logger.info('Shutting down');
});
于 2013-05-15T13:41:38.827 に答える