js非同期入門から放棄(一)-Event Loopモデル


前言


非同期はずっと先端開発の中で最も頭を悩ませる難点であり、次のいくつかの文章は、この話題をめぐって展開される.

1.単一スレッドの言語-JavaScript


JSの当初の目的はブラウザのユーザインタラクションとDOM操作を扱うことであることはよく知られているが,JSが同時に2つ以上のスレッドが存在することを許可するように設計されている場合,以下のような問題が発生する.
2つのスレッドが同じDOMノード(aスレッドはノードを編集し、bスレッドはノードを削除する)を同時に操作すると、ブラウザはどのスレッドを基準にしているか判断できないため、処理できません.したがって,JSは単一スレッドのみである.(Web Worker APIはマルチスレッドを提供しているが、マルチコアcpuを使用する計算能力に純粋に基づいており、その作成されたサブスレッドはJS単一スレッドの設計実質に影響を及ぼさず厳格に制御されている)、単一スレッドの設計は、タスクがキューに並ぶように実行されることを意味する.
単一スレッド設計に基づいて、タスク自体が複雑すぎて処理しにくいのではなく、入力出力が遅すぎる(例えばAjaxがデータを取得する)場合が避けられません.一方,入出力待ちの間は,CPUはアイドル状態であり,リソースを十分に利用するために一時停止を許容し,結果が出てから実行するタスクを許可するように設計されている.
同期タスクと非同期タスクの2つのタスクがあります.
次にJSの処理メカニズムを紹介します.

2. Event Loop


りろんきそ


まずMDNからの図を見てみましょう.

  • スタック(stack)、関数呼び出しスタック.
    この例を見てください.
         function a(){
            console.log('a')
        }
    
        function b(){
            console.log('from')
            a() //        a
        }
        b()
    はChromeで実行され、単一のステップでデバッグされます.次のステップが表示されます.
  • b()を実行すると、関数bが図1のようにスタック
  • に入る.
  • bで関数aを呼び出すと、aは、図2のようにスタック
  • に進む.
  • 関数aの実行が完了し、図1のようにスタックを出る

  • (この部分の内容は,実際には,以前に紹介した閉パケット時の関数作用ドメインチェーンの生成部分,転送ゲートに対応している)

  • スタック(heap)、オブジェクトを格納するメモリ領域.(これは今のところ重要ではありません.

  • キュー(queue)、処理対象メッセージキュー、
    各メッセージには、このメッセージを処理するための関数が関連付けられています.
    一般的な例:
  • ページのいずれかのボタンをクリックするとhandleClick関数がトリガーされ、ユーザがクリックボタンの動作をトリガーすると、処理対象メッセージがqueueに入り、関連する関数はhandleClickである.
  • はajax要求を開始し、要求が結果を出すと、処理対象メッセージがqueueに入り、関連する関数は指定されたコールバック関数
  • である.

    全体運転プロセス


    全体の実行手順は以下の通りです(図のように).
  • メインスレッドは同期コードを実行し、実行プロセスは対応する関数呼び出しスタックstackを生成し、ajax要求が開始されると、対応する非同期モジュール処理にコミットし、非同期タスクに結果がある場合、非同期モジュールはメッセージキューに処理するメッセージを追加する責任を負う.
  • 同期タスク処理が完了し、関数呼び出しスタックが空になった場合、メインスレッドはメッセージキューqueueをチェックする:メッセージキューが空でない場合、メッセージキューヘッダから処理対象のメッセージを取り出し、メインスレッドに入る.
  • メインスレッドは、以上のプロセス
  • を繰り返す.
    上記プロセスループが実行されるので、イベントループ(Event Loop)と呼ぶ
    //      
     var req = new XMLHttpRequest(); 
        req.open('GET', url);    
        req.onload = function (){}; //      ,         ,           api,              
        req.send();

    *タスクキューのタイプ


    補足説明:タスクキューは2つのクラスに分けられます.
  • microtask queue:ES 6のpromiseによって生成されたタスクキュー
  • macrotask queue:microtask queue以外のタスクによって生成されるタスクキュー(イベントトリガsettimeout Ajaxリクエスト)
  • 彼らの違いは次回Promiseを説明するときに説明します(穴を掘る)

    3.タイマー


    上記Event Loopモデルでは、メッセージキューの新しいメッセージソースは、domイベントアクション、ajaxリクエストなどの他に、タイミングタスク、すなわちsetTimeoutによって作成されたタスクであってもよい.この関数はよく知られているに違いないが、必ずしもよく知っているとは限らないかもしれない.setTimeoutは2つのパラメータを受け入れます.
  • コールバック関数
  • が実行を遅らせたミリ秒数.(厳密には、主スレッドに実際に加わる最小遅延時間であるべきであるが、なぜか、下を見る)
  • 次の2つの例を見てみましょう.
    //  1
    console.log(1);
    setTimeout(function(){console.log(2);},1000);
    console.log(3);
    //      1 3 2 ,  setTimeout           1000      

    この例はsettimeoutの基本的な役割を説明しており,比較的簡単ではない.
    //  2
    const s = new Date().getSeconds(); //       
    setTimeout(function() {
      //    "2",           500         
      console.log("Ran after " + (new Date().getSeconds() - s) + " seconds");
    }, 500);
    
    while(true) {//        ,    2s,            
      if(new Date().getSeconds() - s >= 2) { 
        console.log("Good, looped for 2 seconds");
        break;
      }
    }
    
    //    
    Good, looped for 2 seconds
    eventloop.html:15 Ran after 2 seconds

    この例では、まず、setTimeoutを使用して500ミリ秒後に実行されるコールバック関数を指定し、その後、whileサイクルを使用して、現在の実行を2秒以上意図的に実行する.
    実際には500ミリ秒目にメッセージキューにこのメッセージが追加されましたが、現在のプライマリ・スレッドが実行されていないため、呼び出しスタックが空になっていないため、500ミリ秒でsetTimeoutで指定されたコールバック関数は実行されません.実際には、上記のコードの5000に変更しても、結果は同じです.
    簡単に言えば、setTimeout(fn,x )のxは、fnが実行される最小待ち時間を指定するだけであり、既存の呼び出しスタック関数の実行の進捗状況、およびメッセージキュー内の前のタスクの実行の進捗状況に応じて、具体的にどのくらいの時間後に実行できるかを示す.

    小結


    この論文では、JS非同期トピックの基礎編であるEvent Loopモデルプロセスおよび一般的なタスクキューのいくつかのタスクキューメッセージソースについて説明します.
    参考文献:MDN-EventLoopJavaScript実行メカニズム詳細:Event Loopについて
    慣例:内容に間違いがある場合は指摘を歓迎します(理解できないと感じてツッコミを入れても全然大丈夫です);もし助けがあれば、称賛とコレクションを歓迎して、転載して同意を得てから出典を明らかにしてください、もし問題があれば私信の交流を歓迎して、ホームページはメールアドレスがあります