6 lipca 2022

Pętla zdarzeń (event loop) w JavaScripcie

JavaScript jest jednowątkowy: w danej chwili wykonuje tylko jedną operację. Wszystkie żądania obsługuje jeden wątek, ale operacje wejścia/wyjścia przekazuje do innych wątków, dzięki czemu nie blokuje się. Komunikuje się z nimi przez zewnętrzne interfejsy, które potrafią takie wątki tworzyć. Strona internetowa działa więc w jednym wątku, a mimo to może korzystać z innych wątków, które wykonują dla niej konkretne zadania. 

Zobaczmy, z jakich elementów składa się pętla zdarzeń: 

Czym jest stos, a czym kolejka?

Stos to struktura danych, w której to, co trafiło na nią ostatnie, wykonuje się jako pierwsze. 

Kolejka – to, co trafiło do niej pierwsze, wykonuje się jako pierwsze. 

Web API – dodatkowe interfejsy udostępniane przez przeglądarkę. Działają w osobnych wątkach przeglądarki. Należą do nich metody związane z timerami (np. setTimeout), wysyłanie żądań XHR (klasa XMLHttpRequest) oraz obsługa zdarzeń z drzewa DOM i reagowanie na nie.

Za Web API stoją kolejne struktury danych, które decydują, co przeglądarka powinna wykonać najpierw. Mamy wątek główny i wątki poboczne, ale w pewnym momencie trzeba zdecydować, kiedy przekazać wynik do wątku głównego. Jeśli na swoją kolej czekają jednocześnie dwie rzeczy, to właśnie tu rozstrzyga się, która z nich jest ważniejsza. Struktura danych stos: operacje push i pop

STOS | Źródło: https://www.programiz.com/dsa/stack

Struktura danych kolejka: operacje enqueue i dequeue

KOLEJKA | Źródło: https://cdn.programiz.com/sites/tutorial2program/files/queue.png

Kolejki dzielimy na zadania makro, zadania mikro i kolejkę renderowania. Trafiają do nich wywołania funkcji przekazanych jako callbacki do asynchronicznych metod Web API (setTimeout, setInterval, xhr, querySelector). Pętla zdarzeń wykonuje zadania tak długo, jak długo ma co wykonywać: najpierw mikro, potem makro. Przy renderowaniu pętla zdarzeń przetwarza to, co jest w kolejce, i nic poza tym. Jeśli w tym czasie pojawi się coś nowego, musi poczekać. 

Kod synchroniczny i asynchroniczny

Synchroniczny: wykonywany linijka po linijce. W danej chwili może działać tylko jeden fragment kodu. Gdy się zacznie, nie da się zrobić nic innego, dopóki się nie skończy.

Asynchroniczny: może działać równolegle z resztą aplikacji i jej nie zakłóca, ale wynik nie pojawia się od razu. Przykład: zapytanie do API wysyłane w trakcie trwania animacji.

Pętla zdarzeń w JavaScripcie: stos wywołań, Web API i kolejka callbacków

 Źródło: https://kursjs.pl/kurs/intervals/event-loop.php#sync-vs-async

Na schemacie widać trzy główne elementy pętli zdarzeń. Sama pętla to kolejka poleceń do wywołania. Nie ma znaczenia, czy są synchroniczne, czy asynchroniczne – to wszystko akcje, które wykonał użytkownik i które trzeba obsłużyć. Czekają w kolejce i są pobierane po kolei, w kolejności, w jakiej przyszły. Polecenie synchroniczne wykonuje się od razu, a reszta czeka. Przy poleceniu asynchronicznym zapamiętywane jest tylko odwołanie do niego, a samo wykonanie trafia do Web API. Pętla zdarzeń przestaje się nim zajmować – przekazuje jedynie, że to kod asynchroniczny, i „zrób to za mnie, oddaj mi tylko wynik”. Potem dalej pobiera kolejne polecenia z kolejki. To, co oddała do Web API, i tak wraca do kolejki, więc całość działa bez przerwy, w „pętli”. 

Czym jest pętla zdarzeń?

Pętla zdarzeń ustala kolejność, w jakiej wykona się kod. Łączy świat synchroniczny z asynchronicznym. Gdy robimy coś po kolei, wynik mamy od razu. Gdy coś delegujemy, chcemy później móc odebrać jego wartość. Przykład: zapytanie do serwera o dane – nie wystarczy, że dane zostaną pobrane, chcemy je też otrzymać. A skoro kod w tym czasie poszedł dalej, potrzebny jest mechanizm, który odbierze te dane w przyszłości. 

 

Żeby zrozumieć pętlę zdarzeń i wszystkie jej mechanizmy, trzeba odróżnić dwa pojęcia: kod synchroniczny i asynchroniczny. Kod synchroniczny wykonuje się linijka po linijce, nie ma skutków ubocznych i biegnie od A do Z dokładnie tak, jak został wywołany. W kodzie asynchronicznym część pracy na pewnych etapach zostaje oddelegowana do osobnego wątku albo na później. Przykład: chcemy jednocześnie wyświetlać animację i pobierać dane z serwera. W kodzie synchronicznym musielibyśmy albo czekać na dane, albo pokazywać animację. W kodzie asynchronicznym oba zadania mogą się przeplatać. Użytkownik tego nie zauważy, bo całym procesem zarządza przeglądarka. 

O pętli zdarzeń dowiedzieliśmy się więcej dzięki spotkaniom Q-Lab organizowanym przez EMBIQ Community. 

 

 

Może Cię też zainteresować:
Od asystentów do agentów: jak dziś tworzymy oprogramowanie
Oprogramowanie dla IoT i rozwiązań smart (lipiec 2023)
Dlaczego mówimy o AI w procesach, a nie w produktach

Zapytaj o wycenę lub poradę

Administratorem Twoich danych osobowych jest EMBIQ sp. z o.o. (Lublin). Podane przez Ciebie dane przetwarzamy wyłącznie po to, aby odpowiedzieć na Twoje zapytanie, i przechowujemy je nie dłużej niż 24 miesiące. W sprawach dotyczących Twoich danych napisz do nas na adres info@embiq.com.

Przeanalizujmy razem koncepcję Twojego projektu i to, na jakim jest etapie.

Otrzymasz od nas wstępną propozycję zakresu projektu oraz szacunek czasu i kosztów.

Konsultacje są objęte umową NDA.

EMBIQ sp. z o.o. z siedzibą w Lublinie, al. Kraśnicka 27, 20-718 Lublin · Sąd Rejonowy Lublin-Wschód w Lublinie z siedzibą w Świdniku, VI Wydział Gospodarczy Krajowego Rejestru Sądowego · KRS 0000338852 · NIP 9462590420 · kapitał zakładowy 75 000,00 zł