Nodeのイベントサイクル


Nodeのイベントサイクル
フロントブラウザの時間循環がよく分からない場合は、この記事をご覧ください.nodeの中の事件の循環はどうなりますか?実は、公式文書にははっきりした説明があります.まずnodeから単一ファイルを実行してから、事件の循環を説明します.
nodeの内部モジュール
ブラウザのコードはブラウザのエンジンの中にあります.node環境の中にも一定の実行環境があります.まず、公式サイトの依頼カバンにはどんなものがありますか?
  • V 8
  • libuv
  • http-parser
  • c-cares
  • OpenSSL
  • zlib
  • 上はnodejsのモジュールです.これらのモジュールの間ではどうやって働きますか?モジュール間の動作関係は下図のようになります.
    主な過程は以下の通りです.
  • step 1:ユーザーのコードはv 8エンジンのインタプリタを通して、「即時実行」と「非同期実行」の二つの部分に解析された.
  • 直ちに実行します:v 8エンジンの処理に行くコードが必要だと理解できます.非同期実行:真の非同期ではなく、v 8エンジン処理が不要で非同期処理が必要であると理解できる.
  • step 2:「非同期実行」の部分は、v 8エンジンとベース層との間で確立されたバインディング関係を通じて、対応する動作
  • を実行する.
  • step 3:「非同期実行」部分では、libuv内部のイベントサイクル機構により、ブロックコールがない.libuvは実行する時、主にhandlesとrequestを通じて対応の操作を実現して、handlesとrequestsは異なったデータ構造を備えています.公式サイトによると、handlesは長期的に存在する対象であり、requestは短期的に存在する対象であり、予測によると、requestsとhandlesは異なるゴミ回収メカニズムがあるという.
  • libuvのイベントサイクル
    一つのスレッドには唯一のイベントサイクルがあります.スレッドは非安全です
    ここでは2点を理解する必要があります.
  • スレッド
  • これは、Javascriptコードが単一スレッドではないことを理解していますが、libuvは単一スレッドではなく、複数のスレッドを開くことができます.libuvはスケジュールスレッドを提供しています.スレッドの数は、デフォルトでは4つです.最大1024つです.スレッド池については公式文書が見られます.
  • スレッドセキュリティ
  • データの操作は読みと書きにほかならないが、スレッドの安全は簡単には、スレッドがこのデータを独占していて、スレッドが完成したら、他のスレッドが操作できます.もちろん、スレッドの安全の概念はこれらだけではなく、詳しくはウィキペディアを見てもいいです.ここで簡単に理解すればいいです.
    libuvのイベントサイクル
    イベントの循環図は、以下の通りです.
    主に次のステップに分けられます.
  • step 1:スレッドが起動すると、時間を初期化します.now、後のtimerのコールバック関数を計算するためにいつ
  • を実行しますか?
  • step 2:イベントサイクルが生きているかどうかを判断し、生きていないなら、直ちに終了し、さもなければ次のステップに進む.生存するかどうかを判断する根拠:インデックスが存在するかを判断する.インデックスとは、まだ実行するイベントがあるかどうか、要求があるかどうか、イベントのサイクルをオフにする要求などです.(白話で言えば、未処理のことがあるかどうかを見ます)
  • Step 3:すべてのタイマーを実行し、イベントサイクルの前に
  • step 4:実行待ちのコールを実行すると、一般的なIOポーリングはポーリング後に直ちに実行されますが、実行が遅れる場合もあります.実行が遅延される場合は、この段階で
  • を実行します.
  • Step 4:アイドル関数を実行し、各段階で実行されます.通常は必要な操作を実行します.プログラム内蔵の
  • Step 5:準備されたコールバック関数を実行し、具体的に内部で使用される
  • step 6:IOポーリングはタイムアウトまで実行され、ブロック実行前にタイムアウト時間、すなわちポーリング停止時間を計算します.
  • 列が空である場合、または閉じます.または閉じるべきhandlesがあります.timeoutは0
  • です.
  • 上記の状況がなければ、タイムアウトは一番近い時間を取ってください.そうでなければ無限大
  • です.
    (白話で理解すると、閉鎖するものがあるかどうかを見ます.あれば、まっすぐ下に行ってください.なければ、どのような事件があるかを見てから実行します.)
  • Step 7:IO
  • を実行します.
  • step 8:次は何を実行するべきかを確認し、正確に
  • を実行することを保証する.
  • step 9:オフのコールバックがあるかどうか、実行されている場合はループをオフにします.そうでなければ、ループを続けます.
    通常、ファイルのI/Oはスレッドを呼び出すが、ネットワーク要求のI/Oは常に同じスレッドを使用する.
    Nodeのイベントサイクル
    渋滞と渋滞
    nodeのコードはほとんど同期(ブロッキング)と非同期(非ブロッキング)の方式を提供しています.どちらを使うかを選択できますが、混合しないでください.
    nodeの中の事件の循環、1つの簡単版のlibuv事件の循環の構造図です.
    NodeJsのタイマー
    NodeJsのタイマーは主に三つあります.
  • setTimeout
  • set Interval
  • setImmedite
  • 3つのタイマーには対応するキャンセル関数があります.
  • clearTimeout
  • clear Interval
  • clearImmedite
  • set Timeout&set Interval
    setTimeoutとset Intervalの行為はブラウザ環境での行為と似ていますが、setTimeoutとsetImmediateはちょっと違います.libuvではサイクルが終了するかどうかを判断する際に、実行すべき関数がまだあるかどうかを判断する必要があります.setTimeoutまたはset Interval関数が一つしか残っていないなら、サイクル全体はまた継続して存在します.nodeは機能を提供して、サイクルを一時的に休止させることができます.
  • unref
  • ref
  • unrefは、setTimeoutを一時的に休止させることができ、refは再び起動することができる.
    set Immedite
    setImmediteはイベントサイクル終了で実行されることを指定します.主にpoll段階以降に発生します.
    pollキューが空でなければ、列が空になるまで実行します.
    もしpollの列が空きましたら、setImmediteイベントがありましたら、checkの段階にジャンプします.
    もしpollの列が空になったら、setImmediteイベントがないと、どのtimer事件が期限切れになりますか?timersの段階に移ります.
    上の説明に基づいて、setTimeoutとsetImmediateが先着順を実行する問題があります.
    setTimeout(() => {
      console.log('timeout');
    })
    setImmediate(() => {
      console.log('immediate);
    });
    まず答えを言います
            :
    timeout
    immediate
      
    immediate
    timeout
    なぜですか?主にsetTimeoutが前または後の問題であり、スレッドの実行速度に依存している.主に二つの段階です.
  • 、v 8エンジンは環境スキャンコードを実行し、イベントサイクルを開始し、setTimeoutに行くと、timeoutをlibuvイベントキューに投げ込むことができる.
  • 、v 8エンジンは引き続き実行して、setImmediateまで歩きます.
  • この時、上のlibuvイベントキューは初めて実行されるかもしれません.ちょうどpoll段階に入ると、次にimmediateが印刷されます.
  • .
  • は、libuvイベントキューでもあります.もう2回目のサイクルで、poll段階を経て、timeoutが時間になったと判断して、timeoutを実行します.このようにtimeoutを印刷してから、immediate
  • を印刷します.
    その根本的な原因は、事件の循環が一回か二回かにある.
    これから事件の循環のロジックを見ます.
    nextTick
    NodeはこのようなAPIを追加しました.これはイベントサイクルのメカニズムの中ではないですが、時間循環機構と関連しています.まず定義を見てみます.
    nextTickの定義は、イベントサイクルの次の段階の前に対応するコールバックを実行することである.
    nextTickはこのように定義されているが、イベントサイクルの各段階で実行するためではない.主に次の二つの応用シーンがあります.
  • は、次の実行段階のフックとして、不要なリソースを整理したり、
  • を再度要求したりする.
  • などの運転環境が整えたら、再度、コールバック
  • を実行します.
    ケース1:
    let bar;
    
    function someAsyncApiCall(callback) {
      callback()
      process.nextTick(callback);
    }
    
    someAsyncApiCall(() => {
      console.log('bar', bar); // 1
    });
    
    bar = 1;
    
    //   
    undefined
    1
    undefineを出力する場合は、関数を実行する時にbarは割り当てられていないので、process.nextTickは実行環境全体が準備されてから実行することができます.
    ケース2:
    const server = net.createServer();
    server.on('connection', (conn) => { });
    
    server.listen(8080);
    server.on('listening', () => { });
    v 8エンジンがコードを実行した後に、listenのコールバックは直接poll段階に命中して、serverのconnectイベントは実行しません.
    ケース3:
    コンストラクタに対応するイベントを送信したいです.v 8エンジンがまだスキャンされていません.コンストラクタのコードは直ちに実行されます.nextTickが必要です.
    const EventEmitter = require('events');
    const util = require('util');
    
    function MyEmitter() {
      EventEmitter.call(this);
      //       
      this.emit('event');
      //     
      // process.nextTick(() => {
        this.emit('event');
      });
    }
    util.inherits(MyEmitter, EventEmitter);
    
    const myEmitter = new MyEmitter();
    myEmitter.on('event', () => {
      console.log('an event occurred!');
    });
    締め括りをつける
    上の3つの例は、v 8エンジンがシングルラインですぐ実行されることに重点を置いています.libuvは非同期で実行されます.非同期ループの前にいくつかの操作を実行するにはprocess.nextTickが必要です.
    参考文献
    Node公式サイトはlibuvのデザインを説明します.libuvの概念について詳しく説明します.