2017/01/11更新

[Javascript] イベント駆動型の設計ができるEventEmitterに入門

このエントリーをはてなブックマークに追加      

こんにちは、@yoheiMuneです。
今日はNodeJSやフロントJSで利用可能な、EventEmitterを使った、イベント駆動な実装方法をブログに書きたいと思います。
画像

目次




本記事を試すための環境

この記事の内容は、nodejsで動作します。新し目のバージョンでお試しください(僕の環境ではv6.2.2で動作確認しています)。

また、BabelでフロントエンドビルドWebpackでフロントエンドビルドを使えば、フロントエンドでもEventEmitterを利用可能です(いくつかのプロジェクトで利用されているのを見かけたことがあります)。



EventEmitterとは

EventEmitterは直訳の通り、イベントを発火することができる機能で、主に「イベントを発火する人」と「イベントを受け取る人」の大きく2つに使い方が分かれます。具体的には、

イベントを発火する人

関数名 説明
emit イベントを発火してデータを渡す

イベントを受け取る人

関数名 説明
on イベントが発火されたらデータを受け取る
once onと同じだが発火されるのは1回だけ
という感じで使います。説明だけだとわかりづらいと思いますので、コードベースで見てみたいと思います。



EventEmitterの基本的な使い方

コードでは、以下のように利用することができます。
// app.js

// EventEmitterをrequireで読み込む
const EventEmitter = require('events').EventEmitter;

// EventEmitterのインスタンスを作成する
const ev = new EventEmitter();

// 01. イベントを受け取る人
//=======================================
// イベントを受け取る
ev.on('data', data => {
    console.log('dataを受け取ったよ:', data);
})
// イベントを受け取る(1回限り)
ev.once('data', data => {
    console.log('dataを受け取ったよ(1回限り):', data);
});

// イベントを発行する人
//=======================================
ev.emit('data', 1);
ev.emit('data', 2);
ev.emit('data', 3);
上記を実行すると以下のように出力されます。
$ node app.js 
dataを受け取ったよ: 1
dataを受け取ったよ(1回限り): 1
dataを受け取ったよ: 2
dataを受け取ったよ: 3
と、こんな感じでEventEmitterを使うことができます。
ただ上記のような単独で使うことは少ないように思います。EventEmitterを継承したクラスやオブジェクトで使うことが多いと思いますので、次章ではそれを扱ってみたいと思います。



EventEmitterの実践的な使い方

ここではEventEmitterを継承したクラスでの使い方のサンプルを書きたいと思います。以下のクラスは指定したディレクトリ(フォルダ)にあるファイルを読み込むサンプルです。
// app2.js

// EventEmitterをrequireで読み込みます。
const EventEmitter = require('events').EventEmitter;

// ファイル操作に使うモジュールも読み込みます。
const fs = require('fs');
const path = require('path');

//*******************************
// 1.  EventEmitterを継承したクラスを作成します。
//*******************************
class DirectoryReader extends EventEmitter {

    constructor(targetDirectory) {
        super();
        this.dir = targetDirectory;
    }

    read() {
        fs.readdir(this.dir, (err, files) => {

            if (err) {
                //*******************************
                // 2. エラーが発生した場合に、errorイベントを発火します.
                //*******************************
                return this.emit('error', err);
            }

            files.forEach(file => {

                //*******************************
                // 3. データを読み込んだら、dataイベントを発火します。
                //*******************************
                let content = fs.readFileSync(path.join(this.dir, file), 'utf-8');
                this.emit('data', file, content);
            });

            //*******************************
            // 4. 処理が終わったらendイベントを発火します.
            //*******************************
            this.emit('end');
        });
    }
}

//*******************************
// 5. EventEmitterを継承したクラスのインスタンスを生成します.
//*******************************
let dirReader = new DirectoryReader('./sampledir');

//*******************************
// 6. EventEmitterを継承しているので、onでイベントを登録できます.
//*******************************
dirReader.on('error', err => {
    console.log('エラーだよ。 ', err);
});

dirReader.on('data', (fileName, content) => {
    console.log(`データを受け取ったよ。fileName=${fileName}, content=${content}`);
});

dirReader.once('data', (fileName, content) => {
    console.log(`1回だけデータを受け取ったよ。fileName=${fileName}, content=${content}`);
});

dirReader.on('end', () => {
    console.log('終わったよ。');
});

dirReader.read();
上記を実行すると、例えば以下のようになります(サンプルコードは後述のGithubレポジトリにあります)。
$ node app2.js 
データを受け取ったよ。fileName=aaa.txt, content=aaaaaaaaa
1回だけデータを受け取ったよ。fileName=aaa.txt, content=aaaaaaaaa
データを受け取ったよ。fileName=bbb.txt, content=bbbbbbbbbbb
データを受け取ったよ。fileName=ccc.txt, content=ccccccccccccc
終わったよ。
ちょっと長くなってしまいましたが、EventEmitterを継承することでemitonが使えるようになり、イベント駆動な実装ができるようになります。使いこなせるようになると便利ですね。



サンプルコード

上記のサンプルコードは下記にありますので、必要あればお試しください。
https://github.com/yoheiMune/frontend-playground/tree/master/011-eventemitter



参考資料

EventEmitterを学ぶために以下の記事を参考にしました。ありがとうございます。

Events | Node.js v7.4.0 Documentation

node.jsのEventEmitterについてのメモ書き - 株式会社BEFOOL

Node書くならEventEmitterについて知っとくべし - Qiita



最後に

EventEmitterが使われているのは何度かみたことがあったんですが、使うのはほぼ初めてなので学んでみようと思い今回のブログを書きました。JavaScriptは、Promiseを用いた非同期処理での実装が流行っていますが、前からあるEventEmitterもいいですね。それぞれ使いこなせるようになりたい今日この頃です。

最後になりますが本ブログでは、フロントエンド・Go言語・Node.js・Python・Linux・開発関連・Swift・Java・機械学習など雑多に情報発信をしていきます。自分の第2の脳にすべく、情報をブログに貯めています。気になった方は、本ブログのRSSTwitterをフォローして頂けると幸いです ^ ^。

最後までご覧頂きましてありがとうございました!





こんな記事もいかがですか?

RSS画像

もしご興味をお持ち頂けましたら、ぜひRSSへの登録をお願い致します。