ホームページ  >  記事  >  ウェブフロントエンド  >  Node.js_node.jsのイベントエミッターパターンを使用したイベントバインディングの詳細説明

Node.js_node.jsのイベントエミッターパターンを使用したイベントバインディングの詳細説明

WBOY
WBOYオリジナル
2016-05-16 16:39:321398ブラウズ

Node では、多くのオブジェクトがイベントを発行します。たとえば、TCP サーバーは、クライアントが接続を要求するたびに「接続」イベントを発行します。たとえば、データのブロック全体が読み取られるたびに、ファイル システムは「データ」イベントを発行します。これらのオブジェクトは、Node ではイベント エミッターと呼ばれます。イベント エミッタを使用すると、プログラマは関心のあるイベントをサブスクライブし、コールバック関数を関連イベントにバインドできるため、イベント エミッタがイベントを発行するたびにコールバック関数が呼び出されます。パブリッシュ/サブスクライブ モードは、従来の GUI モードと非常によく似ています。たとえば、ボタンがクリックされると、プログラムは対応する通知を受け取ります。このモードを使用すると、サーバー プログラムは、クライアントが接続したとき、ソケットでデータが利用可能になったとき、ファイルが閉じられたときなど、何らかのイベントが発生したときに反応できます。

独自のイベント エミッターを作成することもできます。実際、Node は独自のイベント エミッターを作成するための基本クラスとして使用できる EventEmitter 疑似クラスを提供します。

コールバック パターンを理解する

非同期プログラミングでは、関数呼び出しの終了を示すために関数の戻り値を使用せず、後継の配信スタイルを採用します。

「継続渡しスタイル」(CPS: Continuation-passing style) とは、フロー制御を明示的に次の操作に渡すプログラミングスタイルです...

CPS スタイル関数は、関数を追加パラメータとして受け取ります。この関数は、CPS 関数が「戻り値」を計算するときに、プログラムによって制御される次のプロセスを明示的に示すために使用されます。プログラムの次のステップの関数であり、CPS 関数の「戻り値」をパラメータとして受け取ります。

ウィキペディアより - http://en.wikipedia.org/wiki/Continuation-passing_style

このプログラミング スタイルでは、各関数は実行後にコールバック関数を呼び出し、プログラムの実行を継続できます。後ほど、JavaScript がこのプログラミング スタイルに非常に適していることがわかります。次に、ノードの下のメモリにファイルをロードする例を示します。

コードをコピー コードは次のとおりです:
var fs = require('fs');
fs.readFile('/etc/passwd', function(err, fileContent) {

if (err) {

エラーをスローします;

}

console.log('ファイルコンテンツ', fileContent.toString());

});


この例では、fs.readFile の 2 番目のパラメーターとしてインライン匿名関数を渡しています。これは、後続のプログラム実行のプロセスをコールバック関数に渡すため、実際には CPS を使用したプログラミングです。

ご覧のとおり、コールバック関数の最初のパラメーターはエラー オブジェクトです。プログラムでエラーが発生した場合、このパラメーターは Error クラスのインスタンスになります。これは、Node での CPS プログラミングの一般的なパターンです。

イベント エミッター パターンを理解する

標準コールバック モードでは、関数は実行される関数にパラメーターとして渡されます。このモードは、関数の完了後にクライアントに通知する必要があるシナリオでうまく機能します。ただし、関数の実行中に複数のイベントが発生する場合、またはイベントが複数回繰り返し発生する場合、このモデルは適していません。たとえば、ソケットが利用可能なデータを受信するたびに通知を受け取る必要がある場合、標準のコールバック モードは使いにくいことがわかります。この場合、イベント エミッター モードが便利です。イベント ジェネレーターとイベント リスナーを明確に分離するための標準インターフェイスのセット。

イベント ジェネレーター パターンを使用する場合、イベント エミッターと 1 つ以上のイベント リスナーという 2 つ以上のオブジェクトが関係します。

イベント エミッターは、名前が示すように、イベントを生成できるオブジェクトです。イベント リスナーは、次の例のように、特定の種類のイベントをリッスンするためにイベント エミッターにバインドされたコードです。

コードをコピーします コードは次のとおりです:
var req = http.request(オプション, 関数(応答) {

response.on("データ", function(データ) {

console.log("応答からの一部のデータ", data);

});

response.on("end", function() {

console.log("応答が終了しました");

});

});

req.end();

このコードは、Node の http.request API を使用してリモート HTTP サーバーにアクセスするための HTTP リクエストを作成するために必要な 2 つの手順を示しています (後の章を参照)。 1行目では「継続渡しスタイル」(CPS:Continuation-passing style)を使用し、HTTPレスポンス時に呼び出されるインライン関数を渡しています。 http.request 関数の実行後、プログラムは引き続き後続の操作を実行する必要があるため、HTTP リクエスト API はここで CPS を使用します。

http.request が実行されると、匿名コールバック関数が呼び出され、HTTP 応答オブジェクトがパラメーターとして渡されます。Node ドキュメントによると、この HTTP 応答オブジェクトはイベント エミッターです。多くのイベントでは、イベントが発生するたびに、登録したコールバック関数が呼び出されます。

経験則として、要求された操作の完了後に実行権限を取り戻す必要がある場合は CPS パターンを使用し、イベントが複数回発生する可能性がある場合はイベント エミッター パターンを使用します。

イベントの種類を理解する

発行されたイベントには文字列で表されるタイプがあります。前の例には「data」と「end」という 2 つのイベント タイプが含まれています。これらはイベント エミッターによって定義された任意の文字列ですが、イベント タイプは通常は小文字で構成されます。 Null 文字を含まない単語。

イベント エミッター API にはイントロスペクション メカニズムがないため、コードを使用してイベント エミッターが生成できるイベントの種類を推測することはできません。したがって、使用する API には、発行できるイベントの種類を示すドキュメントが必要です。

イベントが発生すると、イベントエミッターはイベントに関連するリスナーを呼び出し、関連するデータをパラメーターとしてリスナーに渡します。前の http.request の例では、「data」イベント コールバック関数は最初で唯一のパラメータとしてデータ オブジェクトを受け入れますが、「end」はデータを受け入れません。これらのパラメータも API の一部として API 作成者によって決定されます。これらのコールバック関数のパラメーター署名は、主観的に定義されており、各イベント エミッターの API ドキュメントにも記載されています。

イベント エミッターはあらゆる種類のイベントを処理するインターフェイスですが、「エラー」イベントは Node.js の特別な実装です。 Node のほとんどのイベント エミッターは、プログラムでエラーが発生すると「エラー」イベントを生成します。プログラムがイベント エミッターの「エラー」イベントをリッスンしない場合、イベント エミッターはエラーが発生したときにそれを認識してスローします。キャッチされなかった例外。

ノード PERL で次のコードを実行して効果をテストできます。これは 2 つのイベントを生成できるイベント エミッターをシミュレートします。

コードをコピー コードは次のとおりです:
var em = new (require('events').EventEmitter)();
em.emit('event1');

em.emit('error', new Error('私の間違い'));


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

コードをコピーします コードは次のとおりです:
var em = new (require('events').EventEmitter)();
未定義

> em.emit('event1');

> em.emit('エラー', new Error('私の間違い'));

エラー: 私の間違い

返信:1:18

REPLServer.eval (repl.js:80:21)

repl.js:190:20

REPLServer.eval (repl.js:87:5)

インターフェースで (repl.js:182:12)

Interface.emit (events.js:67:17) で

Interface._onLine (readline.js:162:10)

Interface._line (readline.js:426:8)

Interface._ttyWrite (readline.js:603:14)

ReadStream で。 (readline.js:82:12)

>


コードの 2 行目では、「event1」というイベントがランダムに生成されますが、これは何の影響もありません。ただし、「error」イベントが生成されると、エラーがスタックにスローされます。プログラムが PERL コマンド ライン環境で実行されていない場合、プログラムはキャッチされない例外によりクラッシュします。

イベント エミッター API の使用

イベント エミッター パターンを実装するオブジェクト (TCP ソケット、HTTP リクエストなど) は、次のメソッドのセットを実装します。


コードをコピー コードは次のとおりです:
.addListener および .on - 指定されたタイプのイベントのイベント リスナーを追加します
.once - 指定されたタイプのイベントに対して 1 回だけ実行されるイベント リスナーをバインドします
.removeEventListener - 指定されたイベントにバインドされたリスナーを削除します
.removeAllEventListener - 指定されたイベントにバインドされているすべてのリスナーを削除します

以下で詳しくご紹介します。

.addListener() または .on() を使用してコールバック関数

をバインドします

イベントの種類とコールバック関数を指定することで、イベント発生時のアクションを登録できます。たとえば、ファイルがデータ ストリームを読み取るときに使用可能なデータ ブロックがある場合、「データ」イベントが生成されます。次のコードは、コールバック関数を渡すことによってデータ イベントが発生したことをプログラムに通知する方法を示しています。 。

コードをコピーします コードは次のとおりです:

関数受信データ(データ) {

console.log("ファイル読み取りストリームからデータを取得しました: %j", data);

}

readStream.addListener(“データ”, acceptData);

.addListener の短縮形である .on も使用できます。次のコードは上記と同じです。

コードをコピーします コードは次のとおりです:

関数受信データ(データ) {

console.log("ファイル読み取りストリームからデータを取得しました: %j", data);

}
readStream.on(“データ”, acceptData);

前のコードでは、コールバック関数として事前定義された名前付き関数を使用しています。コードを簡素化するためにインライン匿名関数を使用することもできます。

コードをコピーします コードは次のとおりです:
readStream.on("データ", function(data) {
console.log("ファイル読み取りストリームからデータを取得しました: %j", data);

});


前述したように、コールバック関数に渡されるパラメータの数とシグネチャは、特定のイベント エミッタ オブジェクトとイベント タイプに依存します。「データ」イベントはデータ バッファ オブジェクトを渡す場合があり、「エラー」イベントは標準化されていない場合があります。データ バッファ オブジェクトを渡す。エラー オブジェクトを渡すと、データ フローの「終了」イベントはイベント リスナーにデータを渡しません。

複数のイベント リスナーをバインドする

イベント エミッター パターンを使用すると、複数のイベント リスナーが同じイベント エミッターの同じイベント タイプをリッスンできます。次に例を示します。


コードをコピー コードは次のとおりです:
ここにデータがあります。
ここにもデータがあります。


イベント エミッターは、指定されたイベント タイプにバインドされたすべてのリスナーを、リスナーが登録された順序で呼び出す責任があります。

1. イベントが発生したときに、イベント リスナーがすぐに呼び出されない場合があります。その前に他のイベント リスナーが呼び出される可能性があります。

2. 例外がスタックにスローされるのは異常な動作です。イベントが発行されるときに、イベント リスナーが呼び出されたときに例外をスローすると、何らかのイベントが発生する可能性があります。リスナー to が呼び出されることはありません。この場合、イベント エミッターが例外をキャッチし、おそらくそれを処理します。

この例を見てください:


コードをコピー コードは次のとおりです:
readStream.on("データ", function(data) {
throw new Error("何か問題が発生しました");

});

readStream.on("データ", function(データ) {

console.log('ここにもデータがあります。');

});


最初のリスナーが例外をスローしたため、2 番目のリスナーは呼び出されません。

.removeListener() を使用してイベント エミッターからイベント リスナーを削除します

オブジェクトのイベントが不要になった場合は、次のようにイベント タイプとコールバック関数を指定して、登録されたイベント リスナーをキャンセルできます。


コードをコピー コードは次のとおりです:
関数受信データ(データ) {
console.log("ファイル読み取りストリームからデータを取得しました: %j", data);

}

readStream.on("データ", acceptData);

// ...

readStream.removeListener("data", acceptData);


この例では、最後の行は、将来いつでも呼び出される可能性があるイベント エミッター オブジェクトからイベント リスナーを削除します。

リスナーを削除するには、コールバック関数に名前を付ける必要があります。コールバック関数の名前は追加および削除時に必要になるためです。

.once() を使用してコールバック関数を最大 1 回実行します

最大 1 回実行できるイベントを監視したい場合、またはイベントが最初に発生したときのみに関心がある場合は、.once() 関数を使用できます。

コードをコピー コードは次のとおりです:

関数受信データ(データ) {

console.log("ファイル読み取りストリームからデータを取得しました: %j", data);

}

readStream.once("data", acceptData);

上記のコードでは、receiveData 関数は 1 回だけ呼び出されます。 readStream オブジェクトがデータ イベントを発行する場合、receiveData コールバック関数は 1 回だけトリガーされます。

これは、次のように実装が非常に簡単なので、実際には単なる便宜的なメソッドです。

コードをコピーします コードは次のとおりです:

var EventEmitter = require("events").EventEmitter;

EventEmitter.prototype.once = function(type, callback) {

var that = this;

this.on(type, functionlistener() {

that.removeListener(type, リスナー);

callback.apply(that, 引数);

});

};

上記のコードでは、EventEmitter.prototype.once 関数を再定義し、EventEmitter を継承する各オブジェクトの Once 関数も再定義しています。このコードは、イベントを受信すると、.on() メソッドを使用するだけで、.removeEventListener() を使用してコールバック関数の登録をキャンセルし、元のコールバック関数を呼び出します。

注: function.apply() メソッドは前のコードで使用されており、オブジェクトを受け取り、それを含まれるこの変数およびパラメーター配列として使用します。前の例では、変更されていないパラメーター配列が、イベント エミッターを通じてコールバック関数に透過的に渡されます。

.removeAllListeners() を使用してイベント エミッターからすべてのイベント リスナーを削除します

次のように、指定したイベント タイプに登録されているすべてのリスナーをイベント エミッターから削除できます。

コードをコピーします コードは次のとおりです:

エミッター.removeAllListeners(type);

たとえば、次のようにすべてのプロセス割り込み信号リスナーをキャンセルできます。

コードをコピーします コードは次のとおりです:

process.removeAllListeners("SIGTERM");

注: 経験則として、何を削除するかを正確に知っている場合にのみこの関数を使用することをお勧めします。それ以外の場合は、アプリケーションの他の部分にイベント リスナー コレクションを削除させるか、それらの部分に任せることができます。アプリケーションの責任はリスナーを削除することです。ただし、いずれの場合でも、この関数は、イベント エミッターを順序どおりにシャットダウンする準備をしているときや、プロセス全体をシャットダウンする準備をしているときなど、まれなシナリオでは依然として非常に役立ちます。

イベントエミッターの作成

イベント エミッターは、プログラミング インターフェイスをより多用途にするための優れた方法です。一般的でわかりやすいプログラミング パターンでは、クライアントはさまざまな関数を直接呼び出しますが、イベント エミッター パターンでは、クライアントはさまざまなイベントにバインドされます。これにより、プログラムがより柔軟になります。 (翻訳者注: この文はあまり自信がないので、原文を投稿しました: イベント エミッターは、プログラミング インターフェイスをより汎用的にする優れた方法を提供します。共通に理解されているパターンを使用すると、クライアントは関数を呼び出す代わりにイベントにバインドされます。プログラムをより柔軟にします。)

さらに、イベント エミッターを使用すると、無関係な複数のリスナーを同じイベントにバインドするなど、多くの機能も利用できます。

ノードイベントエミッターから継承

Node のイベント エミッター パターンに興味があり、それを独自のアプリケーションで使用する予定がある場合は、EventEmitter を継承して疑似クラスを作成できます。

コードをコピー コードは次のとおりです:

util = require('util');

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

// これは MyClass のコンストラクターです:

var MyClass = function() {

}

util.inherits(MyClass, EventEmitter);

注: util.inherits は、MyClass インスタンスが EventEmitter のプロトタイプ メソッドを使用できるように、MyClass のプロトタイプ チェーンを確立します。

発売イベント

EventEmitter から継承することにより、MyClass は次のようなイベントを発行できます:

コードをコピーします コードは次のとおりです:

MyClass.prototype.someMethod = function() {

this.emit("カスタム イベント", "引数 1", "引数 2");

};

上記のコードでは、MyClass のインスタンスによって someMethod メソッドが呼び出されるときに、「cuteom イベント」というイベントが生成されます。このイベントは、「argument 1」と「argument 2」という 2 つの文字列もデータとして生成します。 、それらはパラメータとしてイベント リスナーに渡されます。

MyClass インスタンスのクライアントは、次のような「カスタム イベント」イベントをリッスンできます:

コードをコピー コードは次のとおりです:

var myInstance = new MyClass();

myInstance.on('カスタム イベント', function(str1, str2) {

console.log('str1 %s および str2 %s のカスタム イベントを取得しました!', str1, str2);

});

別の例として、「tick」イベントを毎秒発行する Ticker クラスを作成できます。

コードをコピーします コードは次のとおりです:

var util = require('util'),

EventEmitter = require('events').EventEmitter;

var Ticker = function() {

var self = this;

setInterval(function() {

self.emit('tick');

}, 1000);

};

util.inherits(Ticker, EventEmitter);

Ticker クラスを使用するクライアントは、Ticker クラスの使用方法を示し、「tick」イベントをリッスンすることができます。

コードをコピーします コードは次のとおりです:

varticker = new Ticker();

ticker.on("tick", function() {

console.log("tick");

});

概要

イベント エミッター パターンは、イベント固有のコードのセットからイベント エミッター オブジェクトを分離するために使用できるリエントラント パターンです。

event_emitter.on() を使用して特定の種類のイベントのリスナーを登録し、event_emitter.removeListener() を使用して登録を解除できます。

EventEmitter を継承し、.emit() 関数を使用するだけで、独自のイベント エミッターを作成することもできます。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。