nodejsのイベント循環メカニズムを深く理解する

4130 ワード

今まで書いてきたJSコードのほとんどはブラウザの環境で運行しています.だからブラウザのイベントサイクルのメカニズムも知っています.macrotaskとmicrotaskの違いが分かります.しかし最近nodeを書く時、nodeの事件の循環の構造とブラウザーの端がとても大きい違いがあることを発見して、特にここで深く勉強しました.
単スレッド
従来のウェブサービスでは、大部分がマルチスレッド機構を使用して、合併の問題を解決しています.原因はI/Oイベントがスレッドをブロックし、ブロックは待ち時間を意味します.nodeのデザインは単スレッドのメカニズムを採用していますが、なぜ高合併の要求が積まれていますか?nodeのシングルパスはメインスレッドだけに対して、つまり各nodeプロセスはメインプログラムだけでプログラムコードを実行していますが、nodeはイベント駆動の機構を採用しています.タイムアウトしたI/O操作をスレッド池のあるスレッドに任せて完了しました.メインスレッド自体は常にスケジュールを調整しているだけで、真のI/O操作を実行していません.つまりnodeが実現したのは非同期非閉塞式です.
イベントサイクル機構
nodeが高合併を実現するコツは、イベントの循環メカニズムにあります.このイベントの循環メカニズムはブラウザ側と似ていますが、多くの違いがあります.nodeの公式紹介によると、nodeは毎回イベント循環メカニズムに6段階が含まれています.
  • timers段階:この段階で既に満了したtimerフィードバック
  • を実行します.
  • I/O calbacks段階:I/O(例えばファイル、ネットワーク)のコールバックを実行する
  • idle,prepareフェーズ:node内部使用
  • poll段階:新しいI/Oイベントを取得し、適切な条件でnodeがここにブロックされます.
  • check段階:setImmediateフィードバックを実行する
  • close calbacks段階:closeイベントのコールバックを実行します.例えば、TCP切断接続
  • 日常開発にとって、私たちが注目しているのはtimers、I/O calbacks、check段階です.nodeとブラウザは明らかに違っています.nodeは各段階が終わったら、すべてのmicrotaskを実行します.この特徴についてはテストができます.
    console.log('main');
    
    setImmediate(function() {
        console.log('setImmediate');
    });
    
    new Promise(function(resolve, reject) {
        resolve();
    }).then(function() {
        console.log('promise.then');
    });
    
    コードの実行結果は:
  • main
  • promise.then
  • setImediate
  • setImmediteとprocess.nextTick
    ブラウザ環境に対して、node環境ではsetImmediteとprocess.nextTickの2つの非同期操作が多くなりました.setImmediteのコールバック関数は、check段階で実行され、即ちイベントサイクルの最終段階に相当します.process.nextTickはmicrotaskとして扱われます.前に述べたように、各段階が終わったら、すべてのmicrotask任務を実行します.だから、process.nextTickは割り込みチームと似たような役割を持っています.次の段階の前に実行できますが、それとpromise.thenはどちらが先に実行しますか?あるコードで実験しました.
    console.log('main');
    
    process.nextTick(function() {
        console.log('nextTick')
    })
    
    new Promise(function(resolve, reject) {
        resolve();
    }).then(function() {
        console.log('promise.then');
    });
    
    コードの実行結果は:
  • main
  • nextTick
  • promise.then
  • process.nextTickの優先度はpromise.thenより高いことが証明されています.
    process.nextTickのハングリートラップ
    process.nextTickは各段階に挿入した後、現在の段階で実行が完了したらすぐに実行できるという利点があります.しかし、この利点は、適切な呼び出しをしないと飢餓トラップに陥りやすいということをも引き起こしている.具体的には、再帰的にprocess.nextTickを呼び出すと、イベントサイクルは次の段階に入ることができず、後段のイベントがずっと実行されなくなり、飢餓問題が発生します.
    例を見れば分かりやすいです.
    let i = 0;
    setImmediate(function() {
        console.log('setImmediate');
    });
    function callback() {
        console.log('nextTick' + i++);
        if (i < 1000) {
            process.nextTick(callback);
        }
    }
    callback();
    
    実行の結果はnextTick 0 nextTick 1 nextTick 2…nextTick 999 setImmediteです.
    setImmediateのフィードバックはprocess.nextTickタスクが完了するまで待ちます.
    結び目
    1.nodeのイベント循環メカニズムとブラウザの違いは、setImmediateとprocess.nextTickの二つの非同期方式を多く出しています.process.nextTickはI/Oの飢餓を招くので、官公庁ではsetImmediateの使用を推奨しています.2.nodeはシングルスレッドの設計ですが、高合併を実現できます.その原因は,主スレッドイベントの循環機構と下のスレッドプールの実現にある.3.このメカニズムは、CPUの密集型アプリケーションには適していないnodeの比較を決定しています.
    最後に書く
    個人的には「先端運搬工」という公衆号を開設しました.優秀な先端精選記事を定期的に送ります.脳の基礎入門がない文章は、違った先端の視角をもたらします.
    転載先:https://juejin.im/post/5bae19ebe51d450e7633241a