イベントベースのプログラミングはいつ使用すべきですか?


65

私は、コールバックを渡すか、プログラムの他の関数から関数をトリガーして、タスクが完了したら物事が起こるようにしました。何かが終了したら、関数を直接トリガーします。

var ground = 'clean';

function shovelSnow(){
    console.log("Cleaning Snow");
    ground = 'clean';
}

function makeItSnow(){
    console.log("It's snowing");
    ground = 'snowy';
    shovelSnow();
}

しかし、私はプログラミングの多くの異なる戦略について読んでおり、私は強力であると理解しているが、まだ練習していないものはイベントベースです(私が読んだ方法は「pub-sub」と呼ばれていました):

var ground = 'clean';

function shovelSnow(){
    console.log("Cleaning Snow");
    ground = 'clean';
}

function makeItSnow(){
    console.log("It's snowing");
    ground = 'snowy';
    $(document).trigger('snow');
}

$(document).bind('snow', shovelSnow);

イベントベースのプログラミングの客観的な長所と短所を理解したいのですが、他の関数内からすべての関数を呼び出すだけです。イベントベースのプログラミングは、どのプログラミング状況で使用する意味がありますか?


2
余談ですが、を使用できます$(document).bind('snow', shovelShow)。無名関数でラップする必要はありません。
カールビーレフェルト14

4
また、イベントリアクティブプログラミングと多くの共通点がある「リアクティブプログラミング」について学習することもできます。
エリックリッパー

回答:


75

イベントは、最近の過去から発生したことを記述した通知です。

イベント駆動型システムの典型的な実装では、イベントディスパッチャーハンドラー関数(またはサブスクライバー)を利用します。ディスパッチャーは、ハンドラーをイベント(jQueryのbind)に接続するAPI と、サブスクライバーにイベントを公開するメソッド(jQueryの)を提供しますtrigger。IOまたはUIイベントについて説明している場合、通常はイベントループもあります。これは、マウスクリックなどの新しいイベントを検出し、それらをディスパッチャーに渡します。JSランドでは、ディスパッチャーとイベントループはブラウザーによって提供されます。

ユーザーと直接やり取りするコード-キー押下とクリックへの応答-では、イベント駆動型プログラミング(または関数型リアクティブプログラミングなどのそのバリエーション)はほとんど避けられません。プログラマーは、いつ、どこでユーザーがクリックするのかわからないので、イベントループでユーザーのアクションを検出し、コードに通知するのはGUIフレームワークまたはブラウザーです。このタイプのインフラストラクチャは、ネットワークアプリケーションでも使用されます(NodeJSを参照)。

関数を直接呼び出すのではなく、コードでイベントを発生させる例には、さらに興味深いトレードオフがあります。これについては、以下で説明します。主な違いは、イベントの発行者(makeItSnow)が呼び出しの受信者を指定しないことです。それは他の場所で結ばれています(bindあなたの例の呼び出しで)。これが呼び出されファイア・アンド・フォーゲットmakeItSnow雪が降っていますことを世界に発表しましたが、聞いています誰が気にしない、次に何が起こるか、それが起こるとき-それは単にメッセージをブロードキャストし、その手をオフダスト。


そのため、イベント駆動型のアプローチでは、メッセージの送信者と受信者を分離します。これにより得られる利点の1つは、特定のイベントに複数のハンドラーがあることです。gritRoads既存のshovelSnowハンドラーに影響を与えることなく、関数をsnowイベントにバインドできます。アプリケーションの接続方法に柔軟性があります。動作をオフにするにはbind、コードを探して動作のすべてのインスタンスを見つけるのではなく、呼び出しを削除するだけです。

イベント駆動型プログラミングのもう1つの利点は、横断的な懸念をどこかに置くことができることです。イベントディスパッチャはMediatorの役割を果たし、一部のライブラリ(Brighterなど)はパイプラインを使用するため、ロギングやサービス品質などの一般的な要件を簡単にプラグインできます。

完全な開示:Brighterは、私が働いているハドルで開発されています。

イベントの送信者を受信者から切り離すことの3番目の利点は、イベントを処理する際の柔軟性が得られることです。各タイプのイベントを独自のスレッドで処理できます(イベントディスパッチャーがサポートしている場合)。または、RabbitMQなどのメッセージブローカーに発生したイベントを配置し、非同期プロセスで処理したり、一晩で一括処理したりすることもできます。イベントの受信者は、別のプロセスまたは別のマシン上にある可能性があります。これを行うためにイベントを発生させるコードを変更する必要はありません!これが「マイクロサービス」アーキテクチャの背後にある大きなアイデアです。自律サービスは、アプリケーションのバックボーンとしてメッセージングミドルウェアとイベントを使用して通信します。

イベントドリブンスタイルのかなり異なる例については、ドメインドリブンデザインを参照してください。ここでは、ドメインイベントを使用して集計を分離します。たとえば、購入履歴に基づいて製品を推奨するオンラインストアを考えてみましょう。の支払いCustomer時に、購入履歴を更新する必要がありますShoppingCartShoppingCart集計は、通知可能性がCustomer高くすることによりCheckoutCompleted、イベントを。Customerイベントに応答して別のトランザクションで更新になるだろう。


このイベント駆動型モデルの主な欠点は、間接指定です。IDEを使用してイベントにナビゲートすることができないため、イベントを処理するコードを見つけるのが難しくなりました。イベントが構成のどこにバインドされているかを把握し、すべてのハンドラーが見つかったことを期待する必要があります。いつでもあなたの頭の中に留めておくべきことがあります。ここでは、コードスタイルの規則が役立ちます(たとえば、すべての呼び出しをbind1つのファイルに入れる)。正気のために、イベントディスパッチャを1つだけ使用し、一貫して使用することが重要です。

別の欠点は、イベントのリファクタリングが難しいことです。イベントの形式を変更する必要がある場合は、すべての受信者も変更する必要があります。これは、ソフトウェアリリースを同期する必要があるため、イベントのサブスクライバーが異なるマシン上にある場合に悪化します!

特定の状況では、パフォーマンスが問題になる場合があります。メッセージを処理するとき、ディスパッチャは次のことを行う必要があります。

  1. いくつかのデータ構造で正しいハンドラーを検索します。
  2. 各ハンドラーのメッセージ処理パイプラインを構築します。これには、大量のメモリ割り当てが含まれる場合があります。
  3. ハンドラーを動的に呼び出します(言語で必要な場合は、おそらくリフレクションを使用します)。

これは、スタックに新しいフレームをプッシュするだけの通常の関数呼び出しよりも確かに遅いです。ただし、イベント駆動型アーキテクチャが提供する柔軟性により、遅いコードの分離と最適化がはるかに容易になります。非同期プロセッサに作業を送信できることは、ハードワークがバックグラウンドで処理されている間、すぐにリクエストに応えることができるため、大きなメリットです。いずれにせよ、DBとやり取りしたり、画面上に物を描画したりすると、IOのコストがメッセージの処理コストを完全に圧倒します。これは、時期尚早な最適化を回避する場合です。


要約すると、イベントは疎結合ソフトウェアを構築するための優れた方法ですが、費用がないわけではありません。たとえば、アプリケーション内のすべての関数呼び出しをイベントに置き換えるのは間違いです。イベントを使用して、意味のあるアーキテクチャ分割を行います。


2
この回答は、正しいと選択した5377の回答と同じです。これをさらに詳しく説明するため、これをマークするように選択を変更しています。
幻想的14

1
速度はイベント駆動型コードの重大な欠点ですか?可能性があるようですが、私にはよくわかりません。
raptortech97

1
@ raptortech97それは確かにあり得ます。特に高速である必要があるコードの場合、内部ループでイベントを送信することは避けたいでしょう。幸いなことに、このような状況では通常、実行する必要があるものが明確に定義されているため、イベント(または用語の異なる同等のメカニズムであるパブリッシュ/サブスクライブまたはオブザーバー)を柔軟に追加する必要はありません。
ジュール

1
また、すべてがメッセージ(イベント)であるアクターモデルを中心に構築されたいくつかの言語(Erlangなど)があることに注意してください。この場合、コンパイラは、メッセージ/イベントを直接関数呼び出しとして実装するか、通信として実装するかを決定できます。
ブレンダン

1
「パフォーマンス」については、シングルスレッドのパフォーマンスとスケーラビリティを区別する必要があると思います。メッセージ/イベントはシングルスレッドのパフォーマンスでは悪化する可能性があります(ただし、追加コストなしで関数呼び出しに変換でき、悪化することはありません)。 -CPUおよび将来の「多数のCPU」システム)。
ブレンダン

25

イベントベースのプログラミングは、プログラムが実行するイベントのシーケンスを制御しない場合に使用されます。代わりに、プログラムフローは、ユーザー(GUIなど)、別のシステム(クライアント/サーバーなど)、または別のプロセス(RPCなど)などの外部プロセスによって指示されます。

たとえば、バッチ処理スクリプトは実行する必要があることを知っているため、実行します。イベントベースではありません

ワープロがそこに座って、ユーザーが入力を開始するのを待ちます。キー押下は、内部ドキュメントバッファを更新する機能をトリガーするイベントです。プログラムは入力する内容を認識できないため、イベント駆動型でなければなりません。

ほとんどのGUIプログラムは、ユーザーの操作に基づいて構築されているため、イベント駆動型です。ただし、イベントベースのプログラムはGUIに限定されません。これは、ほとんどの人にとって最も身近な例です。Webサーバーは、クライアントが接続し、同様のイディオムに従うのを待ちます。コンピューターのバックグラウンドプロセスもイベントに応答する場合があります。たとえば、オンデマンドウイルススキャナは、新しく作成または更新されたファイルに関するイベントをOSから受信し、そのファイルでウイルスをスキャンします。


18

イベントベースのアプリケーションでは、イベントリスナーの概念により、さらに疎結合されたアプリケーションを作成できます。

たとえば、サードパーティのモジュールまたはプラグインは、データベースからレコードを削除し、receordDeletedイベントをトリガーし、残りをイベントリスナーに任せてジョブを実行できます。トリガーモジュールは、この特定のイベントを誰がリッスンしているのか、次に何をすべきかさえ知らない場合でも、すべて正常に機能します。


6

私が追加したかった簡単なアナロジーは私を助けました:

アプリケーションのコンポーネント(またはオブジェクト)は、Facebookの友人の大きなグループと考えてください。

友人の誰かがあなたに何かを伝えたいとき、彼らはあなたに直接電話をかけるか、Facebookのウォールに投稿することができます。彼らがFacebookに投稿すると、誰でも それを見て反応することができますが、多くの人はそうではありません。「私たちは赤ちゃんを産んでいます!」のように、人々がそれに反応する必要がある可能性が高いことが時々重要です。または「まあまあのバンドがDrunkin 'Clamバーでサプライズコンサートをしています!」。最後のケースでは、特に彼らがそのバンドに興味がある場合、残りの友人はおそらくそれに反応する必要があるでしょう。

あなたの友人があなたと彼らの間に秘密を保ちたいと思うなら、彼らはおそらく彼らのFacebookの壁にそれを投稿しないでしょう、彼らはあなたに直接電話してあなたに話します。あなたが好きな女の子に、デートでレストランで会いたいと言っているシナリオを想像してください。彼女に直接電話して尋ねるのではなく、Facebookのウォールに投稿してすべての友達に見せてください。これは機能しますが、あなたがje深い元を持っている場合、彼女はそれを見て、あなたの一日を台無しにするためにレストランに現れることができました。

何かを実装するためにイベントリスナーを組み込むかどうかを決めるときは、このアナロジーについて考えてください。このコンポーネントは、誰でも見ることができるようにビジネスを展開する必要がありますか?または、誰かに直接電話する必要がありますか?物事はかなり簡単に乱雑になる可能性があるので注意してください。


0

この次の例えは、医師の受付で待機線に平行線を引くことにより、イベント駆動型I / Oプログラミングを理解するのに役立つかもしれません。

I / Oをブロックするのは、キューに立っている場合、受付係が目の前の人にフォームに記入するように頼み、彼女が完了するまで待機するようなものです。男がフォームを完成するまで、あなたの番を待つ必要があります。これはブロックされています。

1人の男が記入するのに3分かかる場合、10人目の男は30分まで待たなければなりません。この10番目の人の待ち時間を減らすための解決策は、受付係の数を増やすことです。これは、従来のWebサーバーで発生することです。ユーザー情報を要求する場合、他のユーザーによる後続の要求は、現在の操作(データベースからのフェッチ)が完了するまで待つ必要があります。これにより、10番目のリクエストの「応答時間」が長くなり、n番目のユーザーの指数関数的に増加します。これを回避するために、従来のWebサーバーは、すべての単一のリクエストに対してスレッド(受付の数を増やすことに相当)を作成します。つまり、基本的に、各リクエストにはサーバーのコピーが作成されます。糸。アプリを拡大するには、

イベント駆動型:キューの「応答時間」を拡大するもう1つのアプローチは、イベント駆動型アプローチを採用することです。そこでは、キューにいる人がフォームを引き渡され、記入して完了時に戻ってきます したがって、受付はいつでもリクエストを受け付けます。これは、javascriptが最初から行われていることとまったく同じです。ブラウザでは、javascriptはユーザーのクリックイベント、スクロール、スワイプ、またはデータベースフェッチなどに応答します。javascriptは関数をファーストクラスオブジェクトとして扱い、他の関数にパラメーターとして渡すことができ(コールバックと呼ばれる)、特定のタスクの完了時に呼び出すことができるため、javascriptで本質的に可能です。これは、node.jsがサーバー上で正確に行うことです。あなたはノードのコンテキストで、イベント駆動型プログラミングとI / Oをブロックについての詳細情報を見つけることができ、ここで

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.