promise、process.nextTick、set Timeoutから出発して、Event LoopのJob queueを話します.


一、問題の引き出し
  • イベントloopとは、メインスレッドが「ジョブキュー」からループ読み出しタスク
  • を意味する.
    例1:
    setTimeout(
    	function(){
    		console.log(1);
    	}
    },0);
    console.log(2);
    //  2,1
    
    上記の例では、まずメインスレッドにおける同期タスクを実行し、メインスレッドタスクの実行が完了したら、イベントloopからタスクを読み出すので、先に2を出力してから1を出力します.
  • JavaScriptは、単一スレッドで実行されています.つまり、複数のコードを同時に実行することはできません.あるコードが実行されている場合、後続のすべてのタスクは待ち時間が必要です.現在のタスクが実行されたら、次のタスクをキューから取り出します.これはしばしば「閉塞式執行」と呼ばれる.
  • マウスを一回クリックしたり、タイマーが到着したり、Ajax要求が完了したら、コールバック関数が起動されます.これらのイベントハンドラやコールバック関数はすぐに実行されないです.すぐにキューに並べば、スレッドが空き次第に実行されます.
  • 現在JavaScriptプロセスが実行中である場合、マウスクリックが発生した場合、イベントハンドラがブロックされ、ユーザもすぐにフィードバックを見ることができなくなり、イベントハンドラは前のコードが終わってから実行を開始するまで、タスクキューに入れられます.コードにsetTimeoutが設定されている場合、ブラウザは適切な時間にコードをタスクキューに挿入します.この時間を0にすると、直ちにキューに挿入することを表しますが、すぐに実行するのではなく、前のコードの実行が完了するまで待ちます.
  • setTimeoutは実行時間が保証されていません.JavaScriptスレッドが混雑しているかそれともアイドル状態なのかによって、タイムリーに実行されるかは保証されていません.
  • event loop読み出しジョブの先着順は、ジョブキュー(Job queue)における異なるジョブ読取規則に対する制限
  • に依存する.
    例2:
    setTimeout(function () {
      console.log(3);
    }, 0);
    
    Promise.resolve().then(function () {
      console.log(2);
    });
    
    console.log(1);
    //     1  2 3
    
    先に1を出力するのは、同期タスクがメインスレッド内で優先的に実行されるからですが、ここでの問題は、setTimeoutとPromise.thenタスクの実行優先度がどのように定義されていますか?
    二、Job queueの実行順序
  • Job queueにおけるキューは2つのタイプに分けられています.macro-taskとmicroTask
  • 例えば、実行順序の規定を見ると、設定:macro-taskキューはタスクを含む:a 1,a 2,a 3 micro-taskキューはタスクを含む:b 1,b 2,b 3
    実行順序は、まずマルコ-taskキューの先頭のタスク、つまりa 1タスクを実行し、実行が終わったら、micro-taskキューの中のすべてのタスクを実行します.つまり、b 1,b 2,b 3を順次実行します.実行が終わったら、micro-taskの中のタスクをクリアし、次にマルコ-taskの中の第二のタスクを実行して、順次循環します.
  • は、macro-taskとmicro-taskの2つの列の実行順序を把握した後、リアルなシーンの下でこれらの2つのタイプのキューに本当に含まれるタスク(node V 8エンジンの例)を見に行く
  • .
    node V 8において、これらの2つのタイプの真のタスク順序は以下の通りである.
  • macro-taskキューは本当にタスクを含みます.
  • script(     ),setTimeout, setInterval, setImmediate, I/O, UI rendering
    
  • micro-taskキューは本当にタスクを含みます.
  • process.nextTick, Promises, Object.observe, MutationObserver
    
  • によって得られた実行順序は、
  • であるべきである.
    script(     )—>process.nextTick—>Promises...——>setTimeout——>setInterval——>setImmediate——> I/O——>UI rendering
    
  • ES 6におけるmacro-taskの列はScript Jobsとも呼ばれ、micro-taskはPromiseJobs
  • とも呼ばれています.
    三、実際の環境における実行順序の例
    例3:setTimeoutとpromise
    setTimeout(function () {
      console.log(3);
    }, 0);
    
    Promise.resolve().then(function () {
      console.log(2);
    });
    
    console.log(1);
    
    まず第1小節の例を例にとって、ここで従う順序は以下の通りである.
    script(     )——>promise——>setTimeout
    
    対応する出力は、順次:1——>>2——>3
    例4:process.nextTickとpromise、setTimeout
    setTimeout(function(){console.log(1)},0);
    
    new Promise(function(resolve,reject){
       console.log(2);
       resolve();
    }).then(function(){console.log(3)
    }).then(function(){console.log(4)});
    
    process.nextTick(function(){console.log(5)});
    
    console.log(6);
    //  2,6,5,3,4,1
    
    promiseを定義する際に、promise構造部分は同期して実行されます.
    まずJob queueの実行順序を分析します.
    script(     )——>process.nextTick——>promise——>setTimeout
    
    I)本体部:promiseを定義する構造部分は同期しているので、まず2を出力し、本体部分は更に6を出力する(同期の場合は、厳密に定義された順序に従う)
    II)process.nextTick:出力5
    III)promise:ここのpromiseの部分は、厳密にはpromise.thenの部分で、出力は3,4です.
    IV)setTimeout:最終出力1
    総合的な実行順序:2——>>6——>5——>>3——>4——>1
    例5:より複雑な例
    setTimeout(function(){console.log(1)},0);
    
    new Promise(function(resolve,reject){
       console.log(2);
       setTimeout(function(){resolve()},0)
    }).then(function(){console.log(3)
    }).then(function(){console.log(4)});
    
    process.nextTick(function(){console.log(5)});
    
    console.log(6);
    //      2 6 5 1 3 4
    
    例5と例4の違いはpromiseの構造において同期されていないresoveであるため、promise.thenは現在の実行列には存在せず、プロミセがpendingからresoveに移行してこそthen方法があり、このresoveはsetTimout時間で完成したので、3,4が最後に出力される.