Skoro już wspomnieliśmy o libuv, czas wyjaśnić, skąd w ogóle pojawił się on w kontekście Node.js. ✨
Aby uzyskać efekt asynchroniczności, twórcy Node.js zmuszeni byli wprowadzić mechanizm event loopa, czyli pętli obsługującej zdarzenia nieblokujące głównego wątku.
Jednak pętla sama w sobie nie wie, kiedy ma się uaktywnić. Musi być uruchomiona przez zdarzenie zewnętrzne. Cały proces rozpoczyna się od żądania wykonania operacji asynchronicznej, na przykład odczytu pliku. Takie żądanie trafia do event demultipleksera, gdzie zostaje zarejestrowane. Demultiplekser natychmiastowo potwierdza przyjęcie zadania i odpowiada za przesunięcie żądania do Event Queue w momencie, gdy operacja się zakończy (np. plik zostanie załadowany do pamięci).
Gdy na kolejce zdarzeń pojawia się zestaw do obsłużenia, event loop jest o tym informowany i kolejno wywołuje odpowiednie callbacki.
Gdzie w tym wszystkim znajduje się tajemnicza biblioteka libuv? ⚙️
Otóż wielokrotnie wymieniany event demultiplexer to właśnie niskopoziomowy interfejs systemu operacyjnego, na którym uruchamiana jest nasza aplikacja. Co więcej, każdy system implementuje taki interfejs inaczej. Jest to problematyczne w przypadku Node.js, który z zasady jest multiplatformowy. Twórcy rozwiązali ten problem, ujednolicając interfejsy takie jak epoll (Linux), kqueue (macOS) czy IOCP (Windows) pod postacią biblioteki libuv.
Takie niskopoziomowe API biblioteki normalizuje zachowania różnych systemów, pozwalając Node.js skupić się na obsłudze zdarzeń w ramach samego event loopa, bez konieczności zwracania uwagi na kompatybilność z systemami operacyjnymi.