スタックから分離したイベントループについては説明できないので、次のようにします。
JSには3つの「スタック」があります。
- すべての同期呼び出し用の標準スタック(ある関数が別の関数を呼び出すなど)
- 優先度の高いすべての非同期操作用のマイクロタスクキュー(またはジョブキューまたはマイクロタスクスタック)(process.nextTick、Promises、Object.observe、MutationObserver)
- 優先度の低いすべての非同期操作用のマクロタスクキュー(またはイベントキュー、タスクキュー、マクロタスクキュー)(setTimeout、setInterval、setImmediate、requestAnimationFrame、I / O、UIレンダリング)
|=======|
| macro |
| [...] |
| |
|=======|
| micro |
| [...] |
| |
|=======|
| stack |
| [...] |
| |
|=======|
そしてイベントループはこのように機能します:
- スタックから下から上にすべてを実行し、スタックが空の場合のみ、上のキューで何が起こっているかを確認します
- マイクロスタックをチェックし、スタックの助けを借りて(必要に応じて)すべてを実行します。マイクロタスクキューが空になるか、実行が不要になるまで、マイクロタスクを次々と実行して、マクロスタックのみをチェックします
- マクロスタックをチェックし、スタックの助けを借りてそこで(必要な場合)すべてを実行する
スタックが空でない場合、Micoスタックは接触しません。マイクロスタックが空でないか、実行を必要としない場合、マクロスタックは影響を受けません。
要約すると、マイクロタスクキューはマクロタスクキューとほとんど同じですが、それらのタスク(process.nextTick、Promises、Object.observe、MutationObserver)はマクロタスクよりも優先されます。
マイクロはマクロに似ていますが、優先度が高くなっています。
ここには、すべてを理解するための「究極の」コードがあります。
console.log('stack [1]');
setTimeout(() => console.log("macro [2]"), 0);
setTimeout(() => console.log("macro [3]"), 1);
const p = Promise.resolve();
for(let i = 0; i < 3; i++) p.then(() => {
setTimeout(() => {
console.log('stack [4]')
setTimeout(() => console.log("macro [5]"), 0);
p.then(() => console.log('micro [6]'));
}, 0);
console.log("stack [7]");
});
console.log("macro [8]");
/* Result:
stack [1]
macro [8]
stack [7], stack [7], stack [7]
macro [2]
macro [3]
stack [4]
micro [6]
stack [4]
micro [6]
stack [4]
micro [6]
macro [5], macro [5], macro [5]
--------------------
but in node in versions < 11 (older versions) you will get something different
stack [1]
macro [8]
stack [7], stack [7], stack [7]
macro [2]
macro [3]
stack [4], stack [4], stack [4]
micro [6], micro [6], micro [6]
macro [5], macro [5], macro [5]
more info: https://blog.insiderattack.net/new-changes-to-timers-and-microtasks-from-node-v11-0-0-and-above-68d112743eb3
*/
while (task = todo.shift()) task();