Node.js イベント駆動_node.js

WBOY
リリース: 2016-05-16 15:54:07
オリジナル
1341 人が閲覧しました

Node.js イベント駆動型実装の概要

「イベント」は ECMAScript 標準では明確に定義されていません (また、必須ではありません)。イベントはブラウザーで非常に重要なメカニズムとして機能し、JavaScript に Node.js でのユーザー操作や DOM の変更に応答する機能を提供します。 、非同期イベント駆動モデルは、その高い同時実行機能の基礎です。

JavaScript を学習するには、その実行プラットフォームを理解することも必要です。JavaScript のイベント モデルをより深く理解するために、ノードとブラウザ エンジンのソース コードから始めて、その基礎となる実装を分析し、一連のブログ投稿にまとめていく予定です。 ; これはメモである一方で、分析と理解に漏れや偏りがあれば、修正していただければ幸いです。

イベント駆動型モデルの簡単な説明

JavaScript イベント モデル自体については、すでに多くの優れた記事が説明されていますが、ここでは簡単に説明し、いくつかの優れた記事へのリンクを提供します。

プログラムがイベントにどのように応答するか

私たちのプログラムは、次の 2 つの方法で外部イベントに応答します:

中断

オペレーティング システムは割り込みを通じてキーボードやその他のハードウェア入力を処理します。この方法の利点は、マルチスレッドを使用しなくても、CPU が割り込み信号を受信した後、自動的にコードを実行できることです。対応する割り込みハンドラが完了すると、元のコードの実行環境が復元され、実行が継続されます。この方法にはハードウェアのサポートが必要で、通常はオペレーティング システムによってカプセル化されます。

ポーリング

ループしてイベントが発生したかどうかを検出し、発生した場合は対応するハンドラーを実行します。これは、下位レベルの開発と上位レベルの開発の両方に当てはまります。
Windows ウィンドウ プログラムは、通常メッセージ ループと呼ばれるメイン スレッドに次のコードを記述する必要があります。

MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
  TranslateMessage(&msg);
  DispatchMessage(&msg);
}
ログイン後にコピー
メッセージ ループは、メッセージ (ユーザーの UI 操作、システム メッセージなど) があるかどうかを継続的に検出し、メッセージがある場合は、メッセージを配信し、対応するコールバック関数を呼び出して処理します。

ポーリング方式の欠点の 1 つは、時間のかかる操作がメイン スレッドのメッセージ ループで実行されると、プログラムが新しいメッセージにタイムリーに応答できないことです。これは JavaScript で明らかであり、その解決策とともに後で説明します。

ただし、JavaScript には同様のメッセージ ループ コードはなく、単にイベントを登録して呼び出されるのを待つだけです。これは、ブラウザとノードが実行プラットフォームとしてすでにイベント ループを実装しているため、このプロセスに関与する必要はなく、呼び出し先として静かに待機するだけで済みます。

ノード内のイベントループ

Node ソース コードによるイベント ループの実装を見てみましょう

ノードは JavaScript 実行エンジンとして V8 を使用し、libuv を使用してイベント駆動型の非同期 I/O を実装します。そのイベント ループは、libuv のデフォルトのイベント ループを使用します。

src/node.cc 内、

Environment* env = CreateEnvironment(
    node_isolate,
    uv_default_loop(),
    context,
    argc,
    argv,
    exec_argc,
    exec_argv);
ログイン後にコピー
このコードは、3 行目にある uv_default_loop() を見て、uv ライブラリ自体とその中のdefault_loop_struct を初期化し、それへのポインタを返します。ポインターdefault_loop_ptr。

その後、ノードは実行環境をロードしていくつかのセットアップ操作を完了し、イベント ループを開始します:

bool more;
do {
 more = uv_run(env->event_loop(), UV_RUN_ONCE);
 if (more == false) {
  EmitBeforeExit(env);
  // Emit `beforeExit` if the loop became alive either after emitting
  // event, or after running some callbacks.
  more = uv_loop_alive(env->event_loop());
  if (uv_run(env->event_loop(), UV_RUN_NOWAIT) != 0)
   more = true;
 }
} while (more == true);
code = EmitExit(env);
RunAtExit(env);
...
ログイン後にコピー
more は、次のサイクルに進むかどうかを識別するために使用されます。


env->event_loop() は以前に env に保存されたdefault_loop_ptr を返し、uv_run 関数は指定された UV_RUN_ONCE モードで libuv のイベント ループを開始します。このモードでは、uv_run は少なくとも 1 つのイベントを処理します。つまり、現在のイベント キューに処理する必要がある I/O イベントがない場合、処理する必要がある I/O イベントが存在するまで uv_run はブロックされます。 、または次のタイマー 時間切れです。現在 I/O イベントやタイマー イベントがない場合、uv_run は false を返します。

次のノードは、その他の状況に基づいて次のステップを決定します:

さらに当てはまる場合は、次のループの実行を続けます。

more が false の場合、処理を待機しているイベントがないことを意味します。EmitBeforeExit(env); は、プロセスの「beforeExit」イベントをトリガーし、対応する処理関数をチェックして処理し、ループから直接抜け出します。完成後。

最後に、「exit」イベントがトリガーされ、対応するコールバック関数が実行され、ノード操作が終了し、後でいくつかのリソース解放操作が実行されます。

libuv では、タイマー イベントはイベント ループ内で直接処理されますが、I/O イベントは 2 つのカテゴリに分類されます。

ネットワーク I/O は、Linux の epoll や Windows の IOCP など、システムによって提供されるノンブロッキング I/O ソリューションを使用します。

ファイル操作と DNS 操作には (適切な) システム ソリューションがないため、libuv はブロッキング I/O を実行する独自のスレッド プールを構築しました。

さらに、カスタム関数をスレッド プールにスローして実行することもできます。操作の完了後、メイン スレッドは対応するコールバック関数を実行します。ただし、Node はこの関数を JavaScript に追加していません。ネイティブ ノードを使用して並列実行するために JavaScript で新しいスレッドを開くことは不可能です。

以上がこの記事の全内容です。皆さんに気に入っていただければ幸いです。

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