EventEmitter でイベント駆動オブジェクト on Node.js

あんまりいい例を思いつかなかったけど、タイマーを作ってみた。

  • new Timer でタイマーオブジェクトを作る
  • Timer の プロトタイプオブジェクトにEventEmitterのインスタンスを利用することで、new Timer したオブジェクトはイベント駆動のオブジェクトになる
  • 任意のイベントを登録するには timer.on(eventName, listener) もしくはtimer.addListener(eventName, listener)
  • 任意のイベントリスナーの実行には timer.emit(eventName, [arg1, [arg2...]]) 。この例だと任意のタイミングでリスナーを実行させたいので if (count % 3 === 0) this.emit(eventName, count) みたいにする


このサンプルだと、1秒ごとにカウントを表示する。ただし、「3」がつく数字と「3」で割り切れる数字の場合は "aho" がつく。

var EvEm = require('events').EventEmitter;

function Timer () {
    this.count = 0;
};
(function () {
    Timer.prototype = new EvEm;
    Timer.prototype.run = function () {
        var that = this,
        count    = this.count,
        clearInt = setInterval(function () {
            (count % 3 === 0 || count.toString().match(/3/))
                ? that.emit('aho', count++)
                : console.log(count++);
            return ;
        }, 1000);
        console.log(count++);
    };
})();

var timer = new Timer;
timer.on('aho', function (c) {
    console.log('aho! %s', c);
});

timer.run();

ただ上記場合は、イベントリスナーの登録が1つだけで、他のイベントを複数登録させるには複雑なので、イベントリスナーを登録させるメソッド "setEmt" も実装してみる
この2つ目の例だと、「3」の付く数字と「3」の倍数の時には "aho" となり、「5」の付く数字と「5」の倍数の時には犬になる。

function Timer (interval, defaultFunc) {
    this.count      = 0;
    this.interval   = interval || 1000; // 1秒間隔がデフォルト
    this.eventsList = {};
    this.default    = defaultFunc || function (c) { console.log(c); }; 
};

(function (_tp) {
    _tp.setEmt = function (emtName, pattern) {
        this.eventsList[emtName] = pattern;
        return this;
    };
    _tp.run = function () {
        var that   = this,
        count      = this.count,
        eventsList = this.eventsList,

        clearInt   = setInterval(function () {
            var flg = 0;
            for (var prop in eventsList) {
                if (eventsList[prop](count)) {
                    that.emit(prop, count);
                    flg++;
                }
            }
            if (flg === 0) that.default(count);//console.log(count);
            count++;
            return ;
        }, this.interval);

        console.log(count++);
    };
})(Timer.prototype = new EvEm);

var timer = new Timer;
timer
    .setEmt('aho', function (c) {
        return (c % 3 === 0 || c.toString().match(/3/)) ? true : false;
    })
    .setEmt('dog', function (c) {
        return (c % 5 === 0 || c.toString().match(/5/)) ? true : false;
    })
;

timer
    .on('aho', function (c) {
        console.log('aho! %s', c);
    })
    .on('dog', function (c) {
        console.log('waooon!! %s', c);
    })
;

timer.run();

やってることは単純で、setEmt メソッドで、this.eventsList に eventName と 発火タイミングを条件を返すコールバックを登録しておいて、インターバルタイマーが無名関数を実行するたびに、発火タイミングかどうかを探るというだけ。
毎回ループを回すっていうのはダサダサだからリファクタリングしなくちゃいけないんだけどね!