Bardzo lubię takie sytuacje: próbuję napisać tekst, męczę się niemiłosiernie, nie idzie mi… a wtedy odzywa się do mnie ktoś z czytelników bloga i podrzuca inny temat. Tak właśnie było w przypadku tego wpisu. Jeden z obserwujących fanpage bloga na Facebooku napisał do mnie pytanie, czy mógłbym wrzucić na bloga coś na temat RxJS w kontekście React + Redux. Oczywiście szybko podłapałem i obiecałem, że coś takiego pojawi się wkrótce na blogu! Zanim jednak spróbuję opisać ten dość konkretny przypadek użycia tej biblioteki myślę, że warto najpierw sprawdzić co to jest RxJS. I tym zajmę się w dzisiejszym wpisie!

Co to jest RxJS?

Skrót RxJS pochodzi od “Reactive Extensions for JavaScript” (zachęcam do zajrzenia na stronę projektu RxJS). Po naszemu oznacza to mniej więcej, że jest to zestaw rozszerzeń, które wzbogacają język JavaScript o możliwości programowania reaktywnego. W świecie programowania nie jest to nic nowego. Jako pierwszy, zestaw tego typu rozszerzeń wprowadził Microsoft dla swojej platformy .NET. Główną motywacją przy tworzeniu tych rozszerzeń była chęć ułatwienia programistom pracy z asynchronicznymi strumieniami danych. W rezultacie, dzięki zastosowaniu bibliotek takich jak właśnie RxJS, możliwa jest praca z asynchronicznymi danymi tak jakby były to zwykłe tablice. Stąd też tytuł dzisiejszego posta, ponieważ RxJS to właśnie taki lodash (czy tam underscore) dla strumieni danych asynchronicznych.

W dzisiejszym wpisie na pewno nie przedstawię wszystkich zagadnień związanych z biblioteką RxJS. Ma ona dość spore możliwości i raczej cieżko byłoby zmieścić się w poście o sensownej objętości. Dlatego też dziś pokażę tylko absolutne podstawy, czyli tyle ile potrzeba aby zrozumieć co to jest RxJS.

Instalacja

Oczywiście aby skorzystać z omawianej biblioteki należy w pierwszej kolejności zalinkować ją w naszym projekcie. Najprostszym sposobem jest po prostu umieszczenie stosownego odwołania w pliku index.html:

<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.1/Rx.min.js"></script>

To rzecz jasna najbardziej trywialny sposób. Zapewne w Twoim projekcie wykorzystujesz webpacka lub inny “module bundler”. Wtedy oczywiście użyj sposobu importowania biblioteki właściwego dla Twojej konfiguracji - na przykład poprzez dyrektywę import z ES6.

Obiekt Observable

Generalnie najważniejszym typem danych w RxJS jest obiekt Observable. Reprezentuje on kolekcję wartości w funkcji czasu i można go utworzyć, m.in. za pomocą metody create. Napisałem “m.in.” nie bez powodu - jest całkiem sporo innych metod pozwalających na tworzenie tego obiektu. Mamy też specyficzne obiekty, dziedziczące z Observable. Zresztą więcej przeczytasz w dokumentacji. Spójrz teraz poniżej na przykład tworzenia obiektu Observable za pomocą metody create:

const result = Rx.Observable.create(observer => {
 for(let i = 0; i < 6; i++) {
   observer.next(i);
 }
 observer.complete();
});

Jak widzisz, metoda create jest statycznym “członkiem” obiektu Observable. Wywołując ją, przekazuję do niej funkcję wywołania zwrotnego z jednym parametrem - nazwałem go observer. W ciele klasy widać pętlę, która wykonuje się sześć razy. To co tutaj istotne to wywołanie observer.next(). W ten sposób zwracam kolejne wartości w postaci strumienia danych. Emisję tego strumienia kończę za pomocą wywołania observer.complete().

Subskrypcja obiektu Observable

Wspomniałem wcześniej, że obiekt Observable reprezentuje kolekcję wartości w czasie. Chodzi o to, że odstępy pomiędzy kolejnymi wywołaniami observer.next() mogą być duże. Na przykład wtedy gdy zwracane są wyniki jakichś czasochłonnych operacji. RxJS pozwala nam pracować na takich kolekcjach tak jakby były to statyczne tablice. Zresztą zaraz to pokażę. Najpierw jednak spójrzmy na poniższy kod:

result.subscribe(x => console.log(x));

W powyższy sposób subskrybuję funkcję do obiektu obserwowalnego (funkcja wywołania zwrotnego w stylu “strzałkowym” przekazana jako parametr wywołania funkcji subscribe). Funkcja ta wywołana zostanie dla każdego wywołania observer.next() w obiekcie obserwowalnym. Jak widać, jej zadaniem jest po prostu wyświetlenie danych w konsoli.

RxJS dostarcza nam sporo funkcji wspomagających pracę. Właśnie dzięki nim możemy pracować na strumieniu danych jak na zwykłej tablicy. Spójrz na modyfikację powyższego przykładu:

result.filter(x => x < 4)
 .map(x => `item = ${x}`)
 .subscribe(x => console.log(x));

W powyższym przykładzie, zanim wywołałem funkcję subscribe dokonuję paru modyfikacji strumienia. Najpierw filtruję go za pomocą metody filter, a następnie wynik filtrowania mapuję na tablicę stringów z pomocą funkcji map. Metoda subscribe pracuje już na zmodyfikowanym strumieniu danych. Wyświetli więc w konsoli w kolejnych liniach wartości item = 1, item = 2, item = 3, item = 4.

Przedstawiony kod możesz przetestować w poniższym codepenie:

See the Pen lodash dla danych asynchronicznych czyli wprowadzenie do RxJS by burczu (@burczu) on CodePen.

Podsumowanie - do czego to potrzebne?

Ok, wiemy już co to jest RxJS. Ale do czego może nam się to przydać? Myślę, że w każdej aplikacji znajdą się sytuacje, w których musimy pracować na danych asynchronicznych. Takimi danym są przecież na przykład te, pobierane za pomocą wywołań AJAX - chociażby Angular 2 wykorzystuje obiekty obserwowalne w module http (a także w formularzach). Mamy więc “z pudełka” obsługę rozszerzeń RxJS na wynikach wywołania takich metod jak http.get() czy http.post().

Oczywiście w ten sam sposób można wykorzystywać rozszerzenia reaktywne w aplikacjach React. Myślę, że jest to dobry temat na kolejny wpis - postaram się przygotować go w najbliższym czasie. Zresztą poproszono mnie o to, więc nie mogę zawieść swoich czytelników!