EventEmitter を使用して Node.js でイベントを処理するにはどうすればよいですか?

青灯夜游
リリース: 2020-11-02 17:46:09
転載
2582 人が閲覧しました

EventEmitter を使用して Node.js でイベントを処理するにはどうすればよいですか?

このチュートリアルでは、Node.js のネイティブ EvenEmitter クラスについて学びます。コースを完了すると、イベント、EvenEmitter の使用方法、およびプログラムでのイベントの利用方法を理解できるようになります。さらに、EventEmitter クラスが他のローカル モジュールからどのように拡張されるのかを学び、いくつかの例を通じてその背後にある原則を理解します。

推奨チュートリアル: node js チュートリアル

つまり、この記事では EventEmitter クラスに関するすべてを説明します。

イベントとは何ですか?

イベント駆動型のアーキテクチャは現在非常に一般的であり、イベント駆動型のプログラムはさまざまなイベントを生成、検出し、それに応答できます。

Node.js のコア部分はイベント駆動型であり、ファイル システム (fs) や stream などの多くのモジュール自体が EventEmitter # を使用します。 ## によって書かれた。

イベント駆動型プログラミングでは、

Event (event) は、ユーザーの操作やセンサーのタイミング出力など、1 つ以上のアクションの結果です。

イベント駆動型プログラムは、パブリッシャーがイベントをトリガーし、サブスクライバーがイベントをリッスンして適切なアクションを実行する、パブリッシュ/サブスクライブ モデルとして考えることができます。

たとえば、ユーザーが画像をアップロードできるサーバーがあるとします。イベント駆動型プログラミングでは、画像のアップロードなどのアクションによってイベントが発行され、それを利用するために 1 ~ n 人のサブスクライバーも発生します。

アップロード イベントがトリガーされた後、サブスクライバーは、サイトの管理者に電子メールを送信することで、サイトの管理者に通知し、対応することができます。別のサブスクライバーは、アクションに関する情報を収集し、データベースに保存することができます。

これらのイベントは通常、互いに独立していますが、相互に依存する場合もあります。

イベントエミッターとは何ですか?

EventEmitter クラスは、events モジュールにある Node.js の組み込みクラスです。ドキュメントの説明によると:

Node.js コア API のほとんどは、慣用的な非同期イベント駆動型アーキテクチャに基づいて実装されており、特定の種類のオブジェクト (「エミッター」と呼ばれる) が名前付きイベントを発行します。
Function オブジェクト (「リスナー」) への呼び出しが発生します。"
このクラスは、パブリッシュ/サブスクライブ モデルとしてある程度記述することができます。補助ツールの実装は、イベント エミッター (

Create EventEmitters

そうは言っても、

EventEmitter を作成する方がより現実的です。これを行うには、クラス自体のインスタンスを作成するか、カスタム クラスを通じて実装してから、クラスのインスタンスを作成します。

Create

EventEmitter Object

簡単な例から始めます。プログラムの実行時間情報を含むイベントを毎秒発行する

EventEmitter を作成します。

最初に

events から開始します。モジュール内の EventEmitter クラス: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">const { EventEmitter } = require('events');</pre><div class="contentsignin">ログイン後にコピー</div></div> 次に、

EventEmitter

: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">const timerEventEmitter = new EventEmitter();</pre><div class="contentsignin">ログイン後にコピー</div></div> このオブジェクトを使用してイベントを公開します。簡単:

timerEventEmitter.emit("update");
ログイン後にコピー

イベント名は以前に指定されており、イベントとして発行されました。しかし、まだこのイベントに反応するリスナーがいないため、プログラムは応答しませんでした。

最初にこのイベントを 1 秒ごとに繰り返します。## を使用してください。 #setInterval()

メソッドを使用してタイマーを作成し、

update イベントを 1 秒ごとに発行します:

let currentTime = 0;

// 每秒触发一次 update 事件
setInterval(() => {
    currentTime++;
    timerEventEmitter.emit('update', currentTime);
}, 1000);
ログイン後にコピー
EventEmitter インスタンスはイベント名を受け入れるために使用され、パラメータ。イベント名として

update を渡し、プログラム開始からの時間として currentTime を渡します。 emit( )

メソッドを渡すと、エミッタがトリガーされます。 , これにより、提供された情報を使用してイベントがプッシュされます。イベント エミッターの準備ができたら、イベント リスナーをそれにサブスクライブします:

timerEventEmitter.on('update', (time) => {
    console.log('从发布者收到的消息:');
    console.log(`程序已经运行了 ${time} 秒`);
});
ログイン後にコピー
via on() メソッドはリスナーを作成し、イベントを渡しますリスナーをアタッチするイベントを指定する名前。

update イベントで、時間を記録するメソッドを実行します。 on()

2 番目関数のパラメータは、イベントによって発行された追加データを受け入れることができるコールバックです。

コードを実行すると、次の出力が出力されます:

从发布者收到的消息:
程序已经运行了 1 秒
从发布者收到的消息:
程序已经运行了 2 秒
从发布者收到的消息:
程序已经运行了 3 秒
...
ログイン後にコピー
イベントが初めてトリガーされたときのみ特定の操作を実行する必要がある場合は、

once()

メソッドを使用してサブスクライブすることもできます:

timerEventEmitter.once('update', (time) => {
    console.log('从发布者收到的消息:');
    console.log(`程序已经运行了 ${time} 秒`);
});
ログイン後にコピー
このコードを実行すると、出力:
从发布者收到的消息:
程序已经运行了 1 秒
ログイン後にコピー

EventEmitter

複数のリスナーを使用する場合

以下に別のイベント センダーを作成します。リスナーが3人いるタイマー番組です。最初のリスナーは時間を毎秒更新し、2 番目のリスナーはタイマーが終了するときに起動し、最後のリスナーはタイマーが終了するときに起動します。

update
    : 毎秒一度トリガー
  • #end: カウントダウン終了時にトリガー
  • #end-soon: カウントダウン終了の 2 秒前にトリガー
  • 最初に、このイベント エミッターを作成する関数を作成します: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">const countDown = (countdownTime) =&gt; {     const eventEmitter = new EventEmitter();     let currentTime = 0;     // 每秒触发一次 update 事件     const timer = setInterval(() =&gt; {         currentTime++;         eventEmitter.emit('update', currentTime);         // 检查计时是否已经结束         if (currentTime === countdownTime) {             clearInterval(timer);             eventEmitter.emit('end');         }         // 检查计时是否会在 2 秒后结束         if (currentTime === countdownTime - 2) {             eventEmitter.emit('end-soon');         }     }, 1000);     return eventEmitter; };</pre><div class="contentsignin">ログイン後にコピー</div></div> <p>这个函数启动了一个每秒钟发出一次 <code>update 事件的事件。

    第一个 if 用来检查计时是否已经结束并停止基于间隔的事件。如果已结束将会发布 end 事件。

    如果计时没有结束,那么就检查计时是不是离结束还有 2 秒,如果是则发布 end-soon 事件。

    向该事件发射器添加一些订阅者:

    const myCountDown = countDown(5);
    
    myCountDown.on('update', (t) => {
        console.log(`程序已经运行了 ${t} 秒`);
    });
    
    myCountDown.on('end', () => {
        console.log('计时结束');
    });
    
    myCountDown.on('end-soon', () => {
        console.log('计时将在2秒后结束');
    });
    ログイン後にコピー

    这段代码将会输出:

    程序已经运行了 1 秒
    程序已经运行了 2 秒
    程序已经运行了 3 秒
    计时将在2秒后结束
    程序已经运行了 4 秒
    程序已经运行了 5 秒
    计时结束
    ログイン後にコピー
    ログイン後にコピー

    扩展 EventEmitter

    接下来通过扩展 EventEmitter 类来实现相同的功能。首先创建一个处理事件的 CountDown 类:

    const { EventEmitter } = require('events');
    
    class CountDown extends EventEmitter {
        constructor(countdownTime) {
            super();
            this.countdownTime = countdownTime;
            this.currentTime = 0;
        }
    
        startTimer() {
            const timer = setInterval(() => {
                this.currentTime++;
                this.emit('update', this.currentTime);
        
                // 检查计时是否已经结束
                if (this.currentTime === this.countdownTime) {
                    clearInterval(timer);
                    this.emit('end');
                }
        
                // 检查计时是否会在 2 秒后结束
                if (this.currentTime === this.countdownTime - 2) {
                    this.emit('end-soon');
                }
            }, 1000);
        }
    }
    ログイン後にコピー

    可以在类的内部直接使用 this.emit()。另外 startTimer() 函数用于控制计时开始的时间。否则它将在创建对象后立即开始计时。

    创建一个 CountDown 的新对象并订阅它:

    const myCountDown = new CountDown(5);
    
    myCountDown.on('update', (t) => {
        console.log(`计时开始了 ${t} 秒`);
    });
    
    myCountDown.on('end', () => {
        console.log('计时结束');
    });
    
    myCountDown.on('end-soon', () => {
        console.log('计时将在2秒后结束');
    });
    
    myCountDown.startTimer();
    ログイン後にコピー

    运行程序会输出:

    程序已经运行了 1 秒
    程序已经运行了 2 秒
    程序已经运行了 3 秒
    计时将在2秒后结束
    程序已经运行了 4 秒
    程序已经运行了 5 秒
    计时结束
    ログイン後にコピー
    ログイン後にコピー

    on() 函数的别名是 addListener()。看一下 end-soon 事件监听器:

    myCountDown.on('end-soon', () => {
        console.log('计时将在2秒后结束');
    });
    ログイン後にコピー

    也可以用 addListener() 来完成相同的操作,例如:

    myCountDown.addListener('end-soon', () => {
        console.log('计时将在2秒后结束');
    });
    ログイン後にコピー

    EventEmitter 的主要函数

    eventNames()

    此函数将以数组形式返回所有活动的侦听器名称:

    const myCountDown = new CountDown(5);
    
    myCountDown.on('update', (t) => {
        console.log(`程序已经运行了 ${t} 秒`);
    });
    
    myCountDown.on('end', () => {
        console.log('计时结束');
    });
    
    myCountDown.on('end-soon', () => {
        console.log('计时将在2秒后结束');
    });
    
    console.log(myCountDown.eventNames());
    ログイン後にコピー

    运行这段代码会输出:

    [ 'update', 'end', 'end-soon' ]
    ログイン後にコピー

    如果要订阅另一个事件,例如 myCount.on('some-event', ...),则新事件也会添加到数组中。

    这个方法不会返回已发布的事件,而是返回订阅的事件的列表。

    removeListener()

    这个函数可以从 EventEmitter 中删除已订阅的监听器:

    const { EventEmitter } = require('events');
    
    const emitter = new EventEmitter();
    
    const f1 = () => {
        console.log('f1 被触发');
    }
    
    const f2 = () => {
        console.log('f2 被触发');
    }
    
    emitter.on('some-event', f1);
    emitter.on('some-event', f2);
    
    emitter.emit('some-event');
    
    emitter.removeListener('some-event', f1);
    
    emitter.emit('some-event');
    ログイン後にコピー

    在第一个事件触发后,由于 f1f2 都处于活动状态,这两个函数都将被执行。之后从 EventEmitter 中删除了 f1。当再次发出事件时,将会只执行 f2

    f1 被触发
    f2 被触发
    f2 被触发
    ログイン後にコピー

    An alias for removeListener() is off(). For example, we could have written:

    removeListener() 的别名是 off()。例如可以这样写:

    emitter.off('some-event', f1);
    ログイン後にコピー

    removeAllListeners()

    该函数用于从 EventEmitter 的所有事件中删除所有侦听器:

    const { EventEmitter } = require('events');
    
    const emitter = new EventEmitter();
    
    const f1 = () => {
        console.log('f1 被触发');
    }
    
    const f2 = () => {
        console.log('f2 被触发');
    }
    
    emitter.on('some-event', f1);
    emitter.on('some-event', f2);
    
    emitter.emit('some-event');
    
    emitter.removeAllListeners();
    
    emitter.emit('some-event');
    ログイン後にコピー

    第一个 emit() 会同时触发 f1f2,因为它们当时正处于活动状态。删除它们后,emit() 函数将发出事件,但没有侦听器对此作出响应:

    f1 被触发
    f2 被触发
    ログイン後にコピー

    错误处理

    如果要在 EventEmitter 发出错误,必须用 error 事件名来完成。这是 Node.js 中所有 EventEmitter 对象的标准配置。这个事件必须还要有一个 Error 对象。例如可以像这样发出错误事件:

    myEventEmitter.emit('error', new Error('出现了一些错误'));
    ログイン後にコピー

    error 事件的侦听器都应该有一个带有一个参数的回调,用来捕获 Error 对象并处理。如果 EventEmitter 发出了 error 事件,但是没有订阅者订阅 error 事件,那么 Node.js 程序将会抛出这个 Error。这会导致 Node.js 进程停止运行并退出程序,同时在控制台中显示这个错误的跟踪栈。

    例如在 CountDown 类中,countdownTime参数的值不能小于 2,否则会无法触发 end-soon 事件。在这种情况下应该发出一个 error 事件:

    class CountDown extends EventEmitter {
        constructor(countdownTime) {
            super();
    
            if (countdownTimer < 2) {
                this.emit(&#39;error&#39;, new Error(&#39;countdownTimer 的值不能小于2&#39;));
            }
    
            this.countdownTime = countdownTime;
            this.currentTime = 0;
        }
    
        // ...........
    }
    ログイン後にコピー

    处理这个错误的方式与其他事件相同:

    myCountDown.on(&#39;error&#39;, (err) => {
        console.error('发生错误:', err);
    });
    ログイン後にコピー

    始终对 error 事件进行监听是一种很专业的做法。

    使用 EventEmitter 的原生模块

    Node.js 中许多原生模块扩展了EventEmitter 类,因此它们本身就是事件发射器。

    一个典型的例子是 Stream 类。官方文档指出:

    流可以是可读的、可写的,或两者均可。所有流都是 EventEmitter 的实例。

    先看一下经典的 Stream 用法:

    const fs = require('fs');
    const writer = fs.createWriteStream('example.txt');
    
    for (let i = 0; i < 100; i++) {
      writer.write(`hello, #${i}!\n`);
    }
    
    writer.on(&#39;finish&#39;, () => {
      console.log('All writes are now complete.');
    });
    
    writer.end('This is the end\n');
    ログイン後にコピー

    但是,在写操作和 writer.end() 调用之间,我们添加了一个侦听器。 Stream 在完成后会发出一个 finished 事件。在发生错误时会发出 error 事件,把读取流通过管道传输到写入流时会发出 pipe 事件,从写入流中取消管道传输时,会发出 unpipe 事件。

    另一个类是 child_process 类及其 spawn() 方法:

    const { spawn } = require('child_process');
    const ls = spawn('ls', ['-lh', '/usr']);
    
    ls.stdout.on('data', (data) => {
      console.log(`stdout: ${data}`);
    });
    
    ls.stderr.on('data', (data) => {
      console.error(`stderr: ${data}`);
    });
    
    ls.on('close', (code) => {
      console.log(`child process exited with code ${code}`);
    });
    ログイン後にコピー

    当  child_process 写入标准输出管道时,将会触发  stdoutdata 事件。当输出流遇到错误时,将从 stderr 管道发送 data 事件。

    最後に、プロセスが終了すると、close イベントがトリガーされます。

    概要

    イベント駆動型アーキテクチャにより、高い凝集性と低い結合性を備えたシステムを作成できます。イベントはアクションの結果を表し、1 つ以上のリスナーを定義してそれに反応できます。

    この記事では、EventEmitter クラスとその機能について詳しく説明します。インスタンス化して直接使用し、その動作をカスタム オブジェクトに拡張します。

    最後に、このクラスの重要な機能をいくつか紹介します。

    プログラミング関連の知識について詳しくは、プログラミング コースをご覧ください。 !

以上がEventEmitter を使用して Node.js でイベントを処理するにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:segmentfault.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート