Myślę, że nie możemy omawiać pętli zdarzeń w oddzieleniu od stosu, więc:
JS ma trzy „stosy”:
- standardowy stos dla wszystkich wywołań synchronicznych (jedna funkcja wywołuje inną itd.)
- kolejka mikrozadań (lub kolejka zadań lub stos mikrozadań) dla wszystkich operacji asynchronicznych z wyższym priorytetem (process.nextTick, Promises, Object.observe, MutationObserver)
- kolejka makrotask (lub kolejka zdarzeń, kolejka zadań, kolejka makrozadań) dla wszystkich operacji asynchronicznych z niższym priorytetem (setTimeout, setInterval, setImmediate, requestAnimationFrame, I / O, renderowanie interfejsu użytkownika)
|=======|
| macro |
| [...] |
| |
|=======|
| micro |
| [...] |
| |
|=======|
| stack |
| [...] |
| |
|=======|
A pętla zdarzeń działa w ten sposób:
- wykonuj wszystko od dołu do góry ze stosu i TYLKO gdy stos jest pusty sprawdź co się dzieje w kolejkach powyżej
- sprawdź mikro stos i wykonaj wszystko tam (jeśli jest to wymagane) za pomocą stosu, jedno mikro zadanie po drugim, aż kolejka mikrozadań będzie pusta lub nie będzie wymagała żadnego wykonania, a TYLKO wtedy sprawdź stos makr
- sprawdź stos makr i wykonaj wszystko tam (jeśli jest to wymagane) za pomocą stosu
Stos Mico nie zostanie dotknięty, jeśli stos nie jest pusty. Stos makr nie zostanie dotknięty, jeśli mikro stos nie jest pusty LUB nie wymaga wykonania.
Podsumowując: kolejka mikrozadań jest prawie taka sama jak kolejka makrozadań, ale te zadania (process.nextTick, Promises, Object.observe, MutationObserver) mają wyższy priorytet niż makrozadania.
Micro jest jak makro, ale ma wyższy priorytet.
Tutaj masz „ostateczny” kod do zrozumienia wszystkiego.
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();