JavaScript — to język programowania o jednowątkowej naturze, ale jednocześnie potrafi obsługiwać operacje asynchroniczne. Jest to możliwe dzięki Event Loop — mechanizmowi, który pozwala JavaScript na wykonywanie kodu bez blokowania głównego wątku. W tym wpisie omówimy, jak dokładnie działa Event Loop, dlaczego jest ważny i jak prawidłowo wykorzystać jego cechy.
Podstawy działania Event Loop
W podstawach działania Event Loop leżą trzy główne składniki:
- Call Stack (stos wywołań) — miejsce, gdzie wykonywane są funkcje synchroniczne.
- Task Queue (kolejka zadań) — lista callbacków, które czekają na swoją kolej do wykonania.
- Microtask Queue (kolejka mikrozadań) — kolejka dla Promise i innych mikrozadań.
Rozważmy przykład:
console.log("Początek");
setTimeout(() => {
console.log("setTimeout");
}, 0);
Promise.resolve().then(() => {
console.log("Promise");
});
console.log("Koniec");
Oczekiwany porządek wykonania:
-
console.log("Początek")— wyświetlane od razu. -
setTimeoutodkłada wykonanie callbacka w Task Queue. -
Promise.resolve().then(...)dodaje callback do Microtask Queue. -
console.log("Koniec")— wyświetlane od razu. - Po zakończeniu kodu synchronicznego Event Loop sprawdza Microtask Queue — wykonywane jest
console.log("Promise"). - Dopiero po tym wykonywany jest callback z Task Queue —
console.log("setTimeout").
Wynik w konsoli będzie:
Początek
Koniec
Promise
setTimeout
Dlaczego setTimeout(..., 0) nie wykonuje się natychmiast?
setTimeout(..., 0) nie oznacza, że funkcja wykona się od razu. Dodaje on callback do Task Queue, który jest przetwarzany dopiero po wykonaniu wszystkich mikrozadań.
Przykład, w którym to dobrze widać:
setTimeout(() => console.log("setTimeout"), 0);
for (let i = 0; i < 1e9; i ++) {} // Blokujemy główny wątek
console.log("Koniec pętli");
Tutaj setTimeout jest opóźnione, aż pętla się zakończy, nawet jeśli jest ustawione na 0 milisekund.
Użycie queueMicrotask
Jeśli potrzebujesz wykonać kod wcześniej niż setTimeout, możesz skorzystać z queueMicrotask:
queueMicrotask(() => console.log("Mikrozadanie"));
console.log("Kod główny");
Wynik:
Kod główny
Mikrozadanie