JavaScript事件のメカニズムの実現を初めて見ました.

3959 ワード

ブラウザでは、イベントは極めて重要なメカニズムとして、JavaScriptがユーザ操作とDOMの変化に応答する能力を与える.Node.jsでは、イベント駆動モデルは高合併能力の基礎である.
JavaScriptを学ぶにも、その運用プラットフォームを理解する必要があります.JavaScriptのイベントモデルをよりよく理解するために、Nodeとブラウザのエンジンのソースコードから始めて、その底の実現を分析し、一連のブログに整理します.メモとして、皆さんとコミュニケーションしながら、分析と理解に偏りがあると思います.
イベント駆動モデルを簡単に説明する
JavaScript事件のモデル自体を説明するいい文章がもう多くなりました.これはもう腐った話題です.ここでは簡単に書いて、いい文章のリンクを提供します.
プログラムはどのようにイベントに応答しますか?
私たちのプログラムは外部のイベントに応答して、次の2つの方法があります.
  • 中断操作システムのキーボード処理などのハードウェア入力は中断によって行われることが利点であり、マルチスレッドがなくても安心して私達のコードを実行でき、CPUは中断信号を受信したら自動的に中断処理手順を実行し、処理が完了したら元のコードの実行環境を回復して実行し続けることができる.このような方法はハードウェアのサポートが必要で、一般的にはオペレーティングシステムによって実装されます.
  • ポーリングサイクルは、イベントが発生したかどうかを検出し、ある場合は、対応する処理プログラムを実行する.これは下の層と上の層の開発に応用されています.Windowsウィンドウプログラムは、メインスレッドに次のコードを書く必要があり、通常はメッセージループと呼ばれる.
    MSG msg = { };
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    メッセージループは、メッセージ(ユーザのUI操作、システムメッセージなど)があるかどうかを継続的に検出し、ある場合はメッセージを配信し、対応するコールバック関数を呼び出して処理する.ポーリング方式の欠点の一つは、メインスレッドのメッセージサイクルで時間をつぶして操作すると、プログラムが新しいメッセージにすぐに応答できなくなるということです.これはJavaScriptの中では明らかに表現されています.今後もこの点に言及し、その解決策を検討します.しかし、JavaScriptには同様のメッセージ循環コードがありません.イベントを簡単に登録し、呼び出しを待つだけです.これはブラウザ、Nodeが実行プラットフォームとして、すでにイベントloopを実現しました.JavaScriptコードはこの過程に介入する必要がなく、调节者として静かに待てばいいです.
  • 関連する討論
  • は、事件をブラウザで処理することについて知っていますか?匿名ユーザーの回答:この回答は非常によく、イベントloopの動作原理を理解するのに役立ちます.答えの最後にいくつかの文章があります.
  • MDN-Concerency model and Event Loop:MDN上のevent loopの紹介.
  • Nodeの中のイベントloop
    Nodeのソースコードを通してイベントloopの実現を見ます.
    Nodeは、V 8をJavaScriptの実行エンジンとして、libuvを使ってイベント駆動式非同期I/Oを実現します.そのイベントサイクルは、libuvのデフォルトのイベントサイクルを採用しています.
    src/node.ccでは、
    Environment* env = CreateEnvironment(
            node_isolate,
            uv_default_loop(),
            context,
            argc,
            argv,
            exec_argc,
            exec_argv);
    このコードは、nodeの実行環境を確立し、第3行のuv_default_loop()が見られ、これはlibuvライブラリの関数であり、uvライブラリ自体およびdefault_loop_structを初期化し、ポインタdefault_loop_ptrを返す.その後、Nodeは実行環境をロードし、設定操作を完了し、イベントloopを起動します.
    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のイベントloopを起動します.このようなモードでは、uv_runは、少なくとも1つのイベントを処理することができる.これは、現在のイベントキューに処理が必要なI/Oイベントがない場合、I/Oイベントがあるまで、uv_runはブロックされることを意味する.現在I/Oイベントがなく、タイマーイベントもない場合、uv_runはfalseに戻る.
    次にNodeは、moreの場合に基づいて、次のステップの動作を決定する.
  • は、moretrueであれば、次のloopを継続して実行する.
  • もしmorefalseであるならば、処理待ちのイベントがすでにないと説明し、EmitBeforeExit(env);がプロセスの'beforeExit'イベントをトリガし、対応する処理関数を検査して処理し、完了したら直接ループから飛び出す.
  • 最後に'exit'イベントをトリガし、対応するコールバック関数を実行し、Node実行が終了し、後にいくつかのリソースリリース動作が行われる.
    libuvでは、イベントループのたびに自分のタイムマシンを更新して、タイマー機能を実現します.I/Oイベントは2つに分類されます.
  • Network I/Oは、システムを使用して提供される非ブロッキングI/Oソリューションであり、例えばLinux上でepollを使用し、windows上でIOCPを使用する.
  • ファイル操作とDNS操作にはシステム解決策がないので、libuvはスレッドプールを自己構築し、閉塞式I/Oを行う.
  • また、ユーザー定義の関数をスレッドに投げて実行してもいいです.運行終了後には、メインラインで該当するコールバック関数を実行しますが、Nodeはこの機能をJavaScriptに追加していません.つまり、元のNodeだけでは、JavaScriptで新しいスレッドを開いて並行して実行することはできません.
    関連資料
  • libuv Design Overview:libuvに関するアーキテクチャ及び設計構想.
  • node child_process'exit':Nodeのchild_process'exit事件.
  • Node with threads:libuvスレッド池を使ってJavaScriptコードを非同期的に実行することを議論した.
  • 下一篇:JavaScript事件のメカニズムの実現を初めて見ます.