Node.jsでのイベントエミッターの使用

提供:Dev Guides
移動先:案内検索

著者はCOVID-19救済基金を選択し、 Write forDOnationsプログラムの一環として寄付を受け取りました。

序章

イベントエミッターは、 Node.js 内のオブジェクトであり、アクションが完了したことを通知するメッセージを送信することでイベントをトリガーします。 JavaScript 開発者は、イベントエミッターからイベントをリッスンするコードを記述して、それらのイベントがトリガーされるたびに関数を実行できるようにします。 このコンテキストでは、イベントは、識別文字列とリスナーに渡す必要のあるデータで構成されます。

通常、Node.jsでは、別のアクションの完了時にアクションを発生させたい場合、コールバックのネストやPromiseのチェーンなどの非同期プログラミング手法を使用します。 ただし、これらの手法は、トリガーアクションと結果のアクションを緊密に結合するため、結果のアクションを将来変更することは困難です。 イベントエミッターは、この関係を構築するための別の方法を提供します。publish-subscribeパターンです。 このソフトウェアアーキテクチャパターンでは、パブリッシャー(イベントエミッター)がメッセージ(イベント)を送信し、サブスクライバーがイベントを受信してアクションを実行します。 このパターンの力は、パブリッシャーがサブスクライバーについて知る必要がないことです。 パブリッシャーはメッセージを発行し、それぞれの方法でそれに反応するのはサブスクライバー次第です。 アプリケーションの動作を変更したい場合は、パブリッシャーを変更せずに、サブスクライバーがイベントにどのように反応するかを調整できます。

この記事では、ユーザーがチケットを購入できるようにするTicketManagerJavaScriptクラスのイベントリスナーを作成します。 buyイベントのリスナーを設定します。このイベントは、チケットが購入されるたびにトリガーされます。 このプロセスでは、エミッターからの誤ったイベントを管理する方法と、イベントサブスクライバーを管理する方法も示します。

前提条件

  • 開発マシンにインストールされているNode.js。 このチュートリアルでは、バージョン10.20.1を使用します。 これをmacOSまたはUbuntu18.04にインストールするには、Node.jsをインストールしてmacOSにローカル開発環境を作成する方法またはのPPAを使用したインストール]セクションの手順に従います。 Ubuntu18.04にNode.jsをインストールする方法。
  • この記事の主な例では、 ES2015 (一般にES6と呼ばれます)で導入されたJavaScriptクラスを使用しています。 JavaScriptのクラスについて知りたい場合は、JavaScriptのクラスについてチュートリアルをお読みください。

ステップ1—イベントの送信

このステップでは、Node.jsでイベントエミッターを作成するための2つの最も一般的な方法について説明します。 1つはイベントエミッタオブジェクトを直接使用する方法で、もう1つはイベントエミッタオブジェクトを拡張するオブジェクトを作成する方法です。

どちらを使用するかは、イベントがオブジェクトのアクションにどの程度関連しているかによって異なります。 放出したいイベントがオブジェクトのアクションの効果である場合、便宜上、イベントエミッターオブジェクトからその機能にアクセスできるように拡張する可能性があります。 送信するイベントがビジネスオブジェクトから独立している場合、または多くのビジネスオブジェクトからのアクションの結果である場合は、代わりに、オブジェクトによって参照される独立したイベントエミッタオブジェクトを作成します。

スタンドアロンのイベント発行オブジェクトを作成することから始めましょう。 まず、すべてのコードを保存するフォルダーを作成します。 ターミナルで、event-emittersという名前の新しいフォルダーを作成します。

mkdir event-emitters

次に、そのフォルダに入ります。

cd event-emitters

最初のイベントエミッターfirstEventEmitter.jsをテキストエディターで開きます。 ターミナルで利用できるnanoを使用します。

nano firstEventEmitter.js

Node.jsでは、EventEmitterクラスを介してイベントを発行します。 このクラスはeventsモジュールの一部です。 まず、次の行を追加して、ファイルにeventsモジュールをロードすることから始めましょう。

event-emitters / firstEventEmitter.js

const { EventEmitter } = require("events");

インポートされたクラスを使用して、そこから新しいオブジェクトインスタンスを作成できます。

event-emitters / firstEventEmitter.js

const { EventEmitter } = require("events");

const firstEmitter = new EventEmitter();

firstEventEmitter.jsの最後に次の強調表示された行を追加して、イベントを発行しましょう。

event-emitters / firstEventEmitter.js

const { EventEmitter } = require("events");

const firstEmitter = new EventEmitter();


firstEmitter.emit("My first event");

emit()関数は、イベントを発生させるために使用されます。 イベントの名前を文字列として渡す必要があります。 イベント名の後に引数をいくつでも追加できます。 名前だけのイベントはかなり制限されています。 他の引数を使用すると、リスナーにデータを送信できます。 チケットマネージャーを設定すると、イベントが発生したときに購入に関するデータが渡されます。 イベントリスナーはこの名前でイベントを識別するため、イベントの名前を覚えておいてください。

注:この例ではキャプチャしていませんが、イベントのリスナーがある場合、emit()関数はtrueを返します。 イベントのリスナーがない場合は、falseを返します。


このファイルを実行して、何が起こるかを見てみましょう。 nanoを保存して終了し、nodeコマンドでファイルを実行します。

node firstEventEmitter.js

スクリプトの実行が終了すると、ターミナルに出力が表示されなくなります。 これは、firstEventEmitter.jsにメッセージを記録せず、送信されたイベントをリッスンするものがないためです。 イベントは発行されますが、これらのイベントには何も作用しません。

イベントを公開し、聞いて、それに基づいて行動するより完全な例を見るために働きましょう。 これを行うには、チケットマネージャーのサンプルアプリケーションを作成します。 チケットマネージャーは、チケットを購入する機能を公開します。 チケットを購入すると、購入者の詳細が記載されたイベントが送信されます。 後で、購入を確認する購入者の電子メールに送信される電子メールをシミュレートする別のNode.jsモジュールを作成します。

チケットマネージャーを作成することから始めましょう。 EventEmitterクラスが拡張されるため、イベントを発行するために別のイベントエミッタオブジェクトを作成する必要がありません。

同じ作業ディレクトリで、ticketManager.jsという名前の新しいファイルを作成して開きます。

nano ticketManager.js

最初のイベントエミッターと同様に、eventsモジュールからEventEmitterクラスをインポートする必要があります。 ファイルの先頭に次のコードを配置します。

event-emitters / ticketManager.js

const EventEmitter = require("events");

次に、チケット購入のメソッドをまもなく定義する新しいTicketManagerクラスを作成します。

event-emitters / ticketManager.js

const EventEmitter = require("events");

class TicketManager extends EventEmitter {}

この場合、TicketManagerクラスはEventEmitterクラスを拡張します。 これは、TicketManagerクラスがEventEmitterクラスのメソッドとプロパティを継承することを意味します。 これは、emit()メソッドにアクセスする方法です。

チケットマネージャーでは、購入可能なチケットの初期供給を提供したいと考えています。 これを行うには、コンストラクタ()で初期供給を受け入れます。これは、クラスの新しいオブジェクトが作成されたときに呼び出される特別な関数です。 次のコンストラクターをTicketManagerクラスに追加します。

event-emitters / ticketManager.js

const EventEmitter = require("events");

class TicketManager extends EventEmitter {
    constructor(supply) {
        super();
        this.supply = supply;
    }
}

コンストラクターには1つのsupply引数があります。 これは、私たちが販売できるチケットの最初の供給を詳述する番号です。 TicketManagerEventEmitterの子クラスであると宣言しましたが、EventEmitterのメソッドとプロパティにアクセスするには、super()を呼び出す必要があります。 super()関数は、親関数のコンストラクター(この場合はEventEmitter)を呼び出します。

最後に、this.supplyを使用してオブジェクトのsupplyプロパティを作成し、コンストラクターから渡された値を指定します。

次に、チケットの購入時に呼び出されるbuy()メソッドを追加しましょう。 このメソッドは、供給を1つ減らし、購入データを含むイベントを発行します。

次のようにbuy()メソッドを追加します。

event-emitters / ticketManager.js

const EventEmitter = require("events");

class TicketManager extends EventEmitter {
    constructor(supply) {
        super();
        this.supply = supply;
    }
    
    buy(email, price) {
        this.supply--;
        this.emit("buy", email, price, Date.now());
    }
}

buy()機能では、購入者のメールアドレスとチケットに支払った価格を取得します。 次に、チケットの供給を1つ減らします。 最後に、buyイベントを発行します。 今回は、関数で渡された電子メールと価格、および購入が行われたときのタイムスタンプなどの追加データを含むイベントを発行します。

他のNode.jsモジュールがこのクラスを使用できるようにするには、このクラスをエクスポートする必要があります。 ファイルの最後に次の行を追加します。

event-emitters / ticketManager.js

...

module.exports = TicketManager

ファイルを保存して終了します。

イベントエミッターTicketManagerのセットアップが完了しました。 イベントをプッシュするための準備が整ったので、これらのイベントの読み取りと処理に進むことができます。 そのために、次のステップでイベントリスナーを作成します。

ステップ2—イベントをリッスンする

Node.jsを使用すると、イベントエミッターオブジェクトのon()関数を使用してイベントのリスナーを追加できます。 これは特定のイベント名をリッスンし、イベントがトリガーされたときにコールバックを起動します。 リスナーの追加は通常、次のようになります。

eventEmitter.on(event_name, callback_function) {
    action
}

注::Node.jsは、on()メソッドをaddListener()でエイリアスします。 彼らは同じタスクを実行します。 このチュートリアルでは、引き続きon()を使用します。


私たちの最初のイベントを聞いて、実際に体験してみましょう。 firstListener.jsという名前の新しいファイルを作成します。

nano firstListener.js

on()関数がどのように機能するかを示すために、最初のイベントを受信したときに簡単なメッセージをログに記録しましょう。

まず、TicketManagerを新しいNode.jsモジュールにインポートしましょう。 次のコードをfirstListener.jsに追加します。

event-emitters / firstListener.js

const TicketManager = require("./ticketManager");

const ticketManager = new TicketManager(10);

TicketManagerオブジェクトは、作成時に最初にチケットを提供する必要があることを思い出してください。 これが、10引数を渡す理由です。

次に、最初のNode.jsイベントリスナーを追加しましょう。 buyイベントをリッスンします。 次の強調表示されたコードを追加します。

event-emitters / firstListener.js

const TicketManager = require("./ticketManager");

const ticketManager = new TicketManager(10);

ticketManager.on("buy", () => {
    console.log("Someone bought a ticket!");
});

新しいリスナーを追加するために、ticketManagerオブジェクトの一部であるon()関数を使用しました。 on()メソッドはすべてのイベントエミッタオブジェクトで使用でき、TicketManagerEventEmitterクラスを継承するため、このメソッドはすべてのTicketManagerインスタンスで使用できますオブジェクト。

on()メソッドの2番目の引数は、arrow関数として記述されたコールバック関数です。 この関数のコードは、イベントが発行された後に実行されます。 この場合、buyイベントが発生すると、"Someone bought a ticket!"がコンソールに記録されます。

リスナーを設定したので、buy()関数を使用してイベントを発行します。 ファイルの最後にこれを追加します:

event-emitters / firstListener.js

...

ticketManager.buy("[email protected]", 20);

これは、[email protected]のユーザー電子メールと20のチケット価格でbuyメソッドを実行します。

ファイルを保存して終了します。

次に、nodeを使用してこのスクリプトを実行します。

node firstListener.js

コンソールには次のように表示されます。

OutputSomeone bought a ticket!

最初のイベントリスナーが機能しました。 複数のチケットを購入するとどうなるか見てみましょう。 テキストエディタでfirstListener.jsを再度開きます。

nano firstListener.js

ファイルの最後で、buy()関数をもう一度呼び出します。

event-emitters / firstListener.js

...

ticketManager.buy("[email protected]", 20);
ticketManager.buy("[email protected]", 20);

ファイルを保存して終了します。 Node.jsでスクリプトを実行して、何が起こるかを見てみましょう。

node firstListener.js

コンソールには次のように表示されます。

OutputSomeone bought a ticket!
Someone bought a ticket!

buy()関数が2回呼び出されたため、2つのbuyイベントが発行されました。 私たちのリスナーは両方を拾いました。

イベントが発生するたびではなく、イベントが最初に発生したときにのみ聞くことに関心がある場合があります。 Node.jsは、once()関数を使用して、この場合のon()の代替手段を提供します。

on()と同様に、once()関数は、最初の引数としてイベント名を受け入れ、2番目の引数としてイベントが発生したときに呼び出されるコールバック関数を受け入れます。 内部的には、once()を使用するリスナーがイベントを送受信すると、Node.jsは自動的にリスナーを削除してから、コールバック関数のコードを実行します。

firstListener.jsを編集して、once()の動作を見てみましょう。

nano firstListener.js

ファイルの最後に、once()を使用して、次の強調表示された行のように新しいイベントリスナーを追加します。

event-emitters / firstListener.js

const TicketManager = require("./ticketManager");

const ticketManager = new TicketManager(10);

ticketManager.on("buy", () => {
        console.log("Someone bought a ticket!");
});

ticketManager.buy("[email protected]", 20);
ticketManager.buy("[email protected]", 20);

ticketManager.once("buy", () => {
    console.log("This is only called once");
});

ファイルを保存して終了し、nodeを使用してこのプログラムを実行します。

node firstListener.js

出力は前回と同じです。

OutputSomeone bought a ticket!
Someone bought a ticket!

once()を使用して新しいイベントリスナーを追加しましたが、buyイベントが発行された後に追加されました。 このため、リスナーはこれら2つのイベントを検出しませんでした。 過去に起こった出来事を聞くことはできません。 リスナーを追加すると、その後に続くイベントのみをキャプチャできます。

buy()関数呼び出しをさらにいくつか追加して、once()リスナーが1回だけ反応することを確認できるようにします。 以前のように、テキストエディタでfirstListener.jsを開きます。

nano firstListener.js

ファイルの最後に次の呼び出しを追加します。

event-emitters / firstListener.js

...

ticketManager.once("buy", () => {
    console.log("This is only called once");
});

ticketManager.buy("[email protected]", 20);
ticketManager.buy("[email protected]", 20);

保存して終了し、次のプログラムを実行します。

node firstListener.js

出力は次のようになります。

OutputSomeone bought a ticket!
Someone bought a ticket!
Someone bought a ticket!
This is only called once
Someone bought a ticket!

最初の2行は、once()リスナーが追加される前の最初の2つのbuy()呼び出しからのものです。 新しいイベントリスナーを追加しても以前のイベントリスナーは削除されないため、追加した最初のイベントリスナーは引き続きアクティブであり、メッセージをログに記録します。

on()のイベントリスナーはonce()のイベントリスナーの前に宣言されているため、This is only called onceの前にSomeone bought a ticket!が表示されます。 これらの2つの行は、どちらも最後から2番目のbuyイベントに応答しています。

最後に、buy()への最後の呼び出しが行われたとき、イベントエミッターには、on()で作成された最初のリスナーしかありませんでした。 前述のように、once()で作成されたイベントリスナーがイベントを受信すると、そのイベントは自動的に削除されます。

エミッターを検出するためのイベントリスナーを追加したので、これらのリスナーを使用してデータをキャプチャする方法を見ていきます。

ステップ3—イベントデータのキャプチャ

これまで、発行されたイベントに反応するようにイベントリスナーを設定しました。 放出されたイベントもデータを渡します。 イベントに付随するデータをキャプチャする方法を見てみましょう。

まず、いくつかの新しいNode.jsモジュール(メールサービスとデータベースサービス)を作成します。 これらは、それぞれ電子メールの送信とデータベースへの保存をシミュレートするために使用されます。 次に、それらすべてをメインのNode.jsスクリプト[index.jsで結び付けます。

メールサービスモジュールを編集することから始めましょう。 テキストエディタでファイルを開きます。

nano emailService.js

私たちのメールサービスは、send()という1つのメソッドを含むクラスで構成されています。 このメソッドは、buyイベントとともに発行される電子メールを想定しています。 次のコードをファイルに追加します。

event-emitters / emailService.js

class EmailService {
    send(email) {
        console.log(`Sending email to ${email}`);
    }
}

module.exports = EmailService

このコードは、send()関数を含むEmailServiceクラスを作成します。 実際の電子メールを送信する代わりに、テンプレートリテラルを使用して、チケットを購入した人の電子メールアドレスを含むメッセージをコンソールに記録します。 先に進む前に、保存して終了します。

データベースサービスを設定しましょう。 テキストエディタでdatabaseService.jsを開きます。

nano databaseService.js

データベースサービスは、save()メソッドを介して購入データをデータベースに保存します。 次のコードをdatabaseService.jsに追加します。

event-emitters / databaseService.js

class DatabaseService {
    save(email, price, timestamp) {
        console.log(`Running query: INSERT INTO orders VALUES (email, price, created) VALUES (${email}, ${price}, ${timestamp})`);
    }
}

module.exports = DatabaseService

これにより、単一のsave()メソッドを含む新しいDatabaseServiceクラスが作成されます。 電子メールサービスのsend()メソッドと同様に、save()関数は、buyイベントに付随するデータを使用し、実際にデータベースに挿入するのではなく、コンソールに記録します。 この方法を機能させるには、購入者の電子メール、チケットの価格、およびチケットを購入した時刻が必要です。 ファイルを保存して終了します。

最後のファイルを使用して、TicketManagerEmailService、およびDatabaseServiceをまとめます。 buyイベントのリスナーを設定し、電子メールサービスのsend()関数とデータベースサービスのsave()関数を呼び出します。

テキストエディタでindex.jsファイルを開きます。

nano index.js

最初に行うことは、使用しているモジュールをインポートすることです。

event-emitters / index.js

const TicketManager = require("./ticketManager");
const EmailService = require("./emailService");
const DatabaseService = require("./databaseService");

次に、インポートしたクラスのオブジェクトを作成しましょう。 このデモンストレーションでは、3つの低チケット供給を設定します。

event-emitters / index.js

const TicketManager = require("./ticketManager");
const EmailService = require("./emailService");
const DatabaseService = require("./databaseService");

const ticketManager = new TicketManager(3);
const emailService = new EmailService();
const databaseService = new DatabaseService();

これで、インスタンス化されたオブジェクトを使用してリスナーを設定できます。 誰かがチケットを購入するたびに、データをデータベースに保存するだけでなく、電子メールを送信したいと考えています。 次のリスナーをコードに追加します。

event-emitters / index.js

const TicketManager = require("./ticketManager");
const EmailService = require("./emailService");
const DatabaseService = require("./databaseService");

const ticketManager = new TicketManager(3);
const emailService = new EmailService();
const databaseService = new DatabaseService();


ticketManager.on("buy", (email, price, timestamp) => {
    emailService.send(email);
    databaseService.save(email, price, timestamp);
});

前と同じように、on()メソッドを使用してリスナーを追加します。 今回の違いは、コールバック関数に3つの引数があることです。 各引数は、イベントが発行するデータに対応しています。 念のため、これはbuy()関数のエミッターコードです。

event-emitters / ticketManager.js

this.emit("buy", email, price, Date.now());

コールバックでは、最初にエミッタからemailをキャプチャし、次にprice、最後にDate.now()データをキャプチャし、timestampとしてキャプチャします。

リスナーがbuyイベントを検出すると、emailServiceオブジェクトからsend()関数を呼び出し、databaseServiceからsave()関数を呼び出します。 ]。 この設定が機能することをテストするために、ファイルの最後にあるbuy()関数を呼び出してみましょう。

event-emitters / index.js

...

ticketManager.buy("[email protected]", 10);

エディターを保存して終了します。 次に、nodeを使用してこのスクリプトを実行し、次に何が起こるかを観察してみましょう。 ターミナルに次のように入力します。

node index.js

次の出力が表示されます。

OutputSending email to [email protected]
Running query: INSERT INTO orders VALUES (email, price, created) VALUES ([email protected], 10, 1588720081832)

データは正常にキャプチャされ、コールバック関数に返されました。 この知識があれば、さまざまなイベント名とデータを持つさまざまなエミッターのリスナーを設定できます。 ただし、イベントエミッターを使用したエラーイベントの処理には一定のニュアンスがあります。

次に、エラーイベントを処理する方法と、その際に従う必要のある標準を見てみましょう。

ステップ4—エラーイベントの処理

イベントエミッターがアクションを実行できない場合は、アクションが失敗したことを通知するイベントを発行する必要があります。 Node.jsでは、イベントエミッターが失敗を通知する標準的な方法は、エラーイベントを発行することです。

エラーイベントの名前はerrorに設定する必要があります。 また、Errorオブジェクトを伴う必要があります。 エラーイベントを発行する実際の例を見てみましょう。

チケットマネージャーは、buy()関数が呼び出されるたびに供給を1つ減らします。 今のところ、利用可能なチケットよりも多くのチケットを販売することを妨げるものは何もありません。 buy()関数を変更して、チケットの供給が0に達し、誰かがチケットを購入しようとした場合に、在庫切れを示すエラーが表示されるようにします。

テキストエディタでticketManager.jsをもう一度開きます。

nano ticketManager.js

次に、buy()関数を次のように編集します。

event-emitters / ticketManager.js

...

buy(email, price) {
    if (this.supply > 0) {
        this.supply—;
        this.emit("buy", email, price, Date.now());
        return;
    }

    this.emit("error", new Error("There are no more tickets left to purchase"));
}
...

ifステートメントを追加して、供給がゼロより大きい場合にチケットを購入できるようにしました。 他にチケットがない場合は、errorイベントを発行します。 errorイベントは、このエラーをスローする理由の説明を含む新しいErrorオブジェクトで発行されます。

ファイルを保存して終了します。 index.jsファイルでこのエラーをスローしてみましょう。 現在、チケットは1枚しか購入していません。 ticketManagerオブジェクトを3つのチケットでインスタンス化したため、4つのチケットを購入しようとするとエラーが発生するはずです。

テキストエディタでindex.jsを編集します。

nano index.js

次に、ファイルの最後に次の行を追加して、合計4つのチケットを購入できるようにします。

event-emitters / index.js

...

ticketManager.buy("[email protected]", 10);
ticketManager.buy("[email protected]", 10);
ticketManager.buy("[email protected]", 10);
ticketManager.buy("[email protected]", 10);

エディターを保存して終了します。

このファイルを実行して、何が起こるかを見てみましょう。

node index.js

出力は次のようになります。

OutputSending email to [email protected]
Running query: INSERT INTO orders VALUES (email, price, created) VALUES ([email protected], 10, 1588724932796)
Sending email to [email protected]
Running query: INSERT INTO orders VALUES (email, price, created) VALUES ([email protected], 10, 1588724932812)
Sending email to [email protected]
Running query: INSERT INTO orders VALUES (email, price, created) VALUES ([email protected], 10, 1588724932812)
events.js:196
      throw er; // Unhandled 'error' event
      ^

Error: There are no more tickets left to purchase
    at TicketManager.buy (/home/sammy/event-emitters/ticketManager.js:16:28)
    at Object.<anonymous> (/home/sammy/event-emitters/index.js:17:15)
    at Module._compile (internal/modules/cjs/loader.js:1128:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1167:10)
    at Module.load (internal/modules/cjs/loader.js:983:32)
    at Function.Module._load (internal/modules/cjs/loader.js:891:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
    at internal/main/run_main_module.js:17:47
Emitted 'error' event on TicketManager instance at:
    at TicketManager.buy (/home/sammy/event-emitters/ticketManager.js:16:14)
    at Object.<anonymous> (/home/sammy/event-emitters/index.js:17:15)
    [... lines matching original stack trace ...]
    at internal/main/run_main_module.js:17:47

最初の3つのbuyイベントは正しく処理されましたが、4番目のbuyイベントでプログラムがクラッシュしました。 エラーメッセージの最初を調べてみましょう。

Output...
events.js:196
      throw er; // Unhandled 'error' event
      ^

Error: There are no more tickets left to purchase
    at TicketManager.buy (/home/sammy/event-emitters/ticketManager.js:16:28)
...

最初の2行は、エラーがスローされたことを示しています。 コメントには"Unhandled 'error' event"と書かれています。 イベントエミッターがエラーを発行し、エラーイベントのリスナーをアタッチしなかった場合、Node.jsはエラーをスローし、プログラムをクラッシュさせます。

イベントエミッターをリッスンしている場合は、常にerrorイベントをリッスンすることがベストプラクティスと見なされます。 エラーのリスナーを設定しない場合、1つが発行されると、アプリケーション全体がクラッシュします。 エラーリスナーを使用すると、適切に処理できます。

ベストプラクティスに従うために、エラーのリスナーを設定しましょう。 index.jsファイルを再度開きます。

nano index.js

チケットの購入を開始する前に、リスナーを追加してください。 リスナーは、追加された後に発行されたイベントにのみ応答できることを忘れないでください。 次のようなエラーリスナーを挿入します。

event-emitters / index.js

...

ticketManager.on("error", (error) => {
    console.error(`Gracefully handling our error: ${error}`);
});

ticketManager.buy("[email protected]", 10);
ticketManager.buy("[email protected]", 10);
ticketManager.buy("[email protected]", 10);
ticketManager.buy("[email protected]", 10);

エラーイベントを受信すると、console.error()を使用してコンソールに記録します。

保存してnanoのままにします。 スクリプトを再実行して、エラーイベントが正しく処理されていることを確認します。

node index.js

今回は、次の出力が表示されます。

OutputSending email to [email protected]
Running query: INSERT INTO orders VALUES (email, price, created) VALUES ([email protected], 10, 1588726293332)
Sending email to [email protected]
Running query: INSERT INTO orders VALUES (email, price, created) VALUES ([email protected], 10, 1588726293348)
Sending email to [email protected]
Running query: INSERT INTO orders VALUES (email, price, created) VALUES ([email protected], 10, 1588726293348)
Gracefully handling our error: Error: There are no more tickets left to purchase

最後の行から、エラーイベントが2番目のリスナーによって処理されており、Node.jsプロセスがクラッシュしていないことを確認します。

イベントの送信とリッスンの概念について説明したので、イベントリスナーの管理に使用できるいくつかの追加機能を見てみましょう。

ステップ5—イベントリスナーの管理

イベントエミッターには、イベントにサブスクライブしているリスナーの数を監視および制御するためのメカニズムがいくつか付属しています。 イベントを処理しているリスナーの数の概要を取得するには、すべてのオブジェクトに含まれているlistenerCount()メソッドを使用できます。

listenerCount()メソッドは、カウントするイベント名という1つの引数を受け入れます。 index.jsで実際に動作するのを見てみましょう。

nanoまたは選択したテキストエディタでファイルを開きます。

nano index.js

現在、buy()関数を4回呼び出しています。 それらの4行を削除します。 その場合、index.js全体が次のようになるように、次の2つの新しい行を追加します。

event-emitters / index.js

const TicketManager = require("./ticketManager");
const EmailService = require("./emailService");
const DatabaseService = require("./databaseService");

const ticketManager = new TicketManager(3);
const emailService = new EmailService();
const databaseService = new DatabaseService();

ticketManager.on("buy", (email, price, timestamp) => {
    emailService.send(email);
    databaseService.save(email, price, timestamp);
});

ticketManager.on("error", (error) => {
    console.error(`Gracefully handling our error: ${error}`);
});

console.log(`We have ${ticketManager.listenerCount("buy")} listener(s) for the buy event`);
console.log(`We have ${ticketManager.listenerCount("error")} listener(s) for the error event`);

前のセクションからbuy()の呼び出しを削除し、代わりに2行をコンソールに記録しました。 最初のログステートメントは、listenerCount()関数を使用して、buy()イベントのリスナーの数を表示します。 2番目のログステートメントは、errorイベントのリスナーの数を示しています。

保存して終了。 次に、nodeコマンドを使用してスクリプトを実行します。

node index.js

次の出力が得られます。

OutputWe have 1 listener(s) for the buy event
We have 1 listener(s) for the error event

on()関数は、buyイベントに1回、errorイベントに1回だけ使用したため、この出力は予想と一致します。

次に、listenerCount()を使用して、イベントエミッターからリスナーを削除します。 イベントの期間が適用されなくなったときに、イベントリスナーを削除したい場合があります。 たとえば、チケットマネージャーが特定のコンサートに使用されていた場合、コンサートが終了すると、イベントリスナーを削除します。

Node.jsでは、off()関数を使用して、イベントエミッターからイベントリスナーを削除します。 off()メソッドは、イベント名とそれをリッスンしている関数の2つの引数を受け入れます。

on()関数と同様に、Node.jsはoff()メソッドをremoveListener()でエイリアスします。 それらは両方とも同じことをし、同じ議論をします。 このチュートリアルでは、引き続きoff()を使用します。


off()関数の2番目の引数には、イベントをリッスンしているコールバックへの参照が必要です。 したがって、イベントリスナーを削除するには、そのコールバックを何らかの変数または定数に保存する必要があります。 現状では、off()関数でbuyまたはerrorの現在のイベントリスナーを削除することはできません。

off()の動作を確認するために、以降の呼び出しで削除する新しいイベントリスナーを追加しましょう。 まず、後でoff()で参照できるように、変数でコールバックを定義しましょう。 index.jsnanoで開きます。

nano index.js

index.jsの最後に、次を追加します。

event-emitters / index.js

...

const onBuy = () => {
    console.log("I will be removed soon");
};

次に、buyイベント用に別のイベントリスナーを追加します。

event-emitters / index.js

...

ticketManager.on("buy", onBuy);

そのイベントリスナーが正常に追加されたことを確認するために、buyのリスナー数を出力して、buy()関数を呼び出しましょう。

event-emitters / index.js

...

console.log(`We added a new event listener bringing our total count for the buy event to: ${ticketManager.listenerCount("buy")}`);
ticketManager.buy("test@email", 20);

ファイルを保存して終了し、プログラムを実行します。

node index.js

ターミナルに次のメッセージが表示されます。

OutputWe have 1 listener(s) for the buy event
We have 1 listener(s) for the error event
We added a new event listener bringing our total count for the buy event to: 2
Sending email to test@email
Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email, 20, 1588814306693)
I will be removed soon

出力から、新しいイベントリスナーを追加したときのログステートメントが表示されます。 次に、buy()関数を呼び出し、両方のリスナーがそれに反応します。 最初のリスナーが電子メールを送信してデータをデータベースに保存し、次に2番目のリスナーがメッセージI will be removed soonを画面に出力しました。

次に、off()関数を使用して、2番目のイベントリスナーを削除しましょう。 nanoでファイルを再度開きます。

nano index.js

次に、次のoff()呼び出しをファイルの最後に追加します。 また、リスナーの数を確認するためのログステートメントを追加し、buy()をもう一度呼び出します。

event-emitters / index.js

...

ticketManager.off("buy", onBuy);

console.log(`We now have: ${ticketManager.listenerCount("buy")} listener(s) for the buy event`);
ticketManager.buy("test@email", 20);

onBuy変数がoff()の2番目の引数としてどのように使用されたかに注意してください。 ファイルを保存して終了します。

nodeでこれを実行します。

node index.js

以前の出力は変更されませんが、今回は、リスナーが1つあることを確認するために追加した新しいログ行が見つかります。 buy()が再度呼び出されると、最初のリスナーによって使用されたコールバックの出力のみが表示されます。

OutputWe have 1 listener(s) for the buy event
We have 1 listener(s) for the error event
We added a new event listener bringing our total count for the buy event to: 2
Sending email to test@email
Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email, 20, 1588816352178)
I will be removed soon
We now have: 1 listener(s) for the buy event
Sending email to test@email
Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email, 20, 1588816352178)

off()ですべてのイベントを削除したい場合は、removeAllListeners()関数を使用できます。 この関数は、リスナーを削除するイベントの名前という1つの引数を受け入れます。

ファイルの最後でこの関数を使用して、buyイベント用に追加した最初のイベントリスナーを削除してみましょう。 index.jsファイルをもう一度開きます。

nano index.js

まず、removeAllListeners()を使用してすべてのリスナーを削除します。 次に、listenerCount()関数を使用して、リスナー数を含むステートメントをログに記録します。 聞いていないことを確認するには、別のチケットを購入します。 イベントが発行されると、何も反応しません。

ファイルの最後に次のコードを入力します。

event-emitters / index.js

...

ticketManager.removeAllListeners("buy");
console.log(`We have ${ticketManager.listenerCount("buy")} listeners for the buy event`);
ticketManager.buy("test@email", 20);
console.log("The last ticket was bought");

ファイルを保存して終了します。

次に、nodeコマンドを使用してコードを実行しましょう。

node index.js

最終的な出力は次のとおりです。

We have 1 listener(s) for the buy event
We have 1 listener(s) for the error event
We added a new event listener bringing our total count for the buy event to: 2
Sending email to test@email
Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email, 20, 1588817058088)
I will be removed soon
We now have: 1 listener(s) for the buy event
Sending email to test@email
Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email, 20, 1588817058088)
We have 0 listeners for the buy event
The last ticket was bought

すべてのリスナーを削除した後、チケット購入のために電子メールを送信したり、データベースに保存したりすることはなくなりました。

結論

このチュートリアルでは、Node.jsイベントエミッターを使用してイベントをトリガーする方法を学習しました。 EventEmitterオブジェクトのemit()関数を使用してイベントを発行し、on()およびonce()関数を使用してイベントをリッスンして、イベントが発生するたびにコードを実行します。引き金になった。 また、エラーイベントのリスナーを追加し、listenerCount()機能を使用してリスナーを監視および管理しました。

コールバックとPromiseを使用する場合、同じ機能を取得するには、チケットマネージャーシステムを電子メールおよびデータベースサービスモジュールと統合する必要があります。 イベントエミッターを使用したため、イベントは実装から切り離されました。 さらに、チケットマネージャにアクセスできるモジュールは、そのイベントを監視してそれに反応することができます。 内部または外部のNode.jsモジュールでオブジェクトで何が起こっているかを監視する場合は、スケーラビリティーのためにイベントエミッターにすることを検討してください。

Node.jsのイベントの詳細については、Node.jsのドキュメントをご覧ください。 Node.jsの学習を継続したい場合は、 Node.jsシリーズのコーディング方法に戻るか、Nodeトピックページでプログラミングプロジェクトとセットアップを参照してください。