Co to jest programowanie reaktywne?

Programowanie reaktywne to paradygmat programowania skoncentrowany na strumieniach danych i propagacji zmian. Zamiast pisać kod, który explicite pobiera i przetwarza dane krok po kroku, opisujesz, jak dane powinny przepływać i transformować się w czasie. Gdy wartości się zmieniają, aktualizacje automatycznie propagują się przez aplikację. Można to porównać do arkusza kalkulacyjnego: gdy zaktualizujesz komórkę A1, wszystkie komórki odwołujące się do A1 automatycznie przeliczają się. To podejście doskonale radzi sobie z asynchronicznymi zdarzeniami, takimi jak interakcje użytkownika, odpowiedzi HTTP, wiadomości WebSocket i timery.

Jak działa RxJS?

RxJS (Reactive Extensions for JavaScript) implementuje programowanie reaktywne poprzez Observables — obiekty emitujące wartości w czasie. Główne pojęcia to:

  1. Observable: Źródło danych, które emituje wartości (np. strumień zdarzeń kliknięcia lub odpowiedzi API)
  2. Observer: Konsument reagujący na emitowane wartości za pomocą callbacków (next, error, complete)
  3. Subscription: Połączenie między Observable a Observer
  4. Operators: Funkcje transformujące, filtrujące, łączące lub manipulujące strumieniami danych
  5. Subjects: Specjalne Observable, które działają zarówno jako źródło danych, jak i konsument, umożliwiając multicast

Tworzysz potoki danych, łącząc operatory metodą pipe(), przekształcając surowe strumienie w dokładnie taki kształt danych, jaki potrzebuje Twoja aplikacja.

Opis narzędzia

RxJS Playground to interaktywny sandbox działający w przeglądarce, umożliwiający eksperymentowanie z koncepcjami programowania reaktywnego RxJS. Piszesz i wykonujesz kod RxJS bezpośrednio w edytorze, mając pełny dostęp do biblioteki RxJS i wszystkich operatorów. Playground uruchamia Twój kod automatycznie podczas pisania (z debounce), wyświetlając wyjście konsoli w panelu stylizowanym na terminal. To idealne rozwiązanie do nauki koncepcji RxJS, prototypowania transformacji danych, debugowania potoków Observable oraz testowania kombinacji operatorów bez konieczności konfigurowania środowiska programistycznego.

Funkcje

  • Pełny dostęp do biblioteki RxJS: Wszystkie funkcje tworzenia (of, from, interval, fromEvent itp.) i operatory dostępne
  • Wykonanie w czasie rzeczywistym: Kod uruchamia się automatycznie z opóźnieniem 500 ms podczas pisania
  • Wyjście terminalowe: Logi konsoli, błędy i ostrzeżenia wyświetlane w stylizowanym terminalu
  • Podświetlanie składni: Edytor kodu JavaScript z odpowiednim podświetlaniem składni
  • Wyświetlanie wartości zwracanej: Końcowe wartości wyrażeń automatycznie logowane ze strzałką
  • Obsługa błędów: Czytelne komunikaty o błędach składni i czasu wykonywania
  • Moduł operatorów: Bezpośredni dostęp do rxjs/operators dla transformacji opartych na pipe

Przykłady

Podstawowe tworzenie Observable:

const { of, from, interval } = rxjs;

// Emit individual values
of(1, 2, 3).subscribe((x) => console.log(x));

// Convert array to Observable
from([10, 20, 30]).subscribe((x) => console.log(x));

Użycie operatorów:

const { of } = rxjs;
const { map, filter, take } = operators;

of(1, 2, 3, 4, 5)
  .pipe(
    filter((x) => x % 2 === 0),
    map((x) => x * 10),
  )
  .subscribe((x) => console.log(x));
// Output: 20, 40

Łączenie strumieni:

const { of, merge, concat } = rxjs;

const stream1 = of("A", "B");
const stream2 = of("X", "Y");

merge(stream1, stream2).subscribe((x) => console.log("merge:", x));
concat(stream1, stream2).subscribe((x) => console.log("concat:", x));

Subjecty do multicastingu:

const { Subject, BehaviorSubject } = rxjs;

const subject = new Subject();
subject.subscribe((v) => console.log("Observer 1:", v));
subject.subscribe((v) => console.log("Observer 2:", v));
subject.next(1);
subject.next(2);

Potok transformacji:

const { from } = rxjs;
const { map, reduce, toArray } = operators;

from([1, 2, 3, 4, 5])
  .pipe(
    map((x) => x * 2),
    toArray(),
  )
  .subscribe((arr) => console.log("Doubled:", arr));

Dostępne moduły

rxjs (główny moduł):

  • Tworzenie: of, from, interval, timer, range, generate, defer, iif
  • Łączenie: merge, concat, combineLatest, forkJoin, zip, race
  • Subjecty: Subject, BehaviorSubject, ReplaySubject, AsyncSubject
  • Narzędzia: firstValueFrom, lastValueFrom, isObservable

operators (operatory pipe'owalne):

  • Transformacja: map, pluck, scan, reduce, buffer, switchMap, mergeMap, concatMap
  • Filtrowanie: filter, take, takeUntil, skip, debounceTime, throttleTime, distinctUntilChanged
  • Łączenie: withLatestFrom, combineLatestWith, mergeWith, startWith
  • Obsługa błędów: catchError, retry, retryWhen
  • Narzędzia: tap, delay, timeout, toArray, share, shareReplay

Przypadki użycia

  • Nauka RxJS: Zrozumienie koncepcji Observable poprzez praktyczne eksperymenty
  • Prototypowanie przepływów danych: Projektowanie i testowanie reaktywnych potoków danych przed implementacją
  • Debugowanie operatorów: Izolowanie i weryfikacja zachowania operatorów w uproszczonych przykładach
  • Przygotowanie do rozmowy kwalifikacyjnej: Ćwiczenie wzorców programowania reaktywnego i typowych problemów
  • Demonstracje edukacyjne: Pokazywanie koncepcji reaktywnych przy użyciu żywych, interaktywnych przykładów