RxJS – share operator

Ciężko nie zauważyć, że budowanie aplikacji w Angular, łączy się z ciągłą pracą w bibliotece RxJS. Stwierdziłem, że od czasu do czasu, skrobnę art o strumieniach. Myślę również o jakimś artykule typu RxJS w pigułce, dla początkujących developerów Angulara. W pierwszym wpisie o RxJS, zapraszam Cię do zapoznania się z operatorem share().

Grupy operatorów

Zanim przejdę do analizy operatora share, przypomnimy sobie ogólny podział operatorów wraz z przykładami:

  • Filtrowanie:  filter, first, last, debounce, skipUntil…
  • Transformowanie: map, switchMap, scan, pluck…
  • Obsługa błędów: catchError, retry…
  • Tzw. utilsy, o różnym przeznaczeniu: tap, finally, toPromise…
  • Multicasting: publish, share, shareReplay…

Część operatorów jest dużo bardziej popularna od pozostałych, chociażby ciężko spotkać aplikację angularową, w której nie został wykorzystany operator map lub catchError. Być może o grupie multicasting słyszysz po raz pierwszy. Jeśli tak, to już spieszę się z wyjaśnieniem ;).

Operatory z grupy multicasting, pozwalają między innymi na współdzielenie efektów ubocznych przez wielu subskrybentów, konwertują również observable ze stanu cold na hot przy spełnieniu określonych warunków (domyślnie observable są cold z pewnymi wyjątkami, np. fromEvent(input, ‚click’), który jest hot – polecam poczytać artykuł na ten temat – cold vs hot observables). Najłatwiej będzie nam to zrozumieć na przykładzie operatora share.

Share operator

Share pozwala na współdzielenie zasobów przez wielu subskrybentów.
Zobaczmy prosty przykład:

W powyższym przykładzie jest oczywiste, że nasz side-effect w postaci loga ‚I want to be called only once’  wyświetli się dwa razy i faktycznie tak się dzieje, no bo przecież mamy użyte 2x subscribe na jednym strumieniu. Może być niefajnie, jeśli byłaby to bardziej znacząca funkcja z określoną logiką.

Załóżmy, że chcemy wyłącznie raz wywołać side-effect, mimo wielu subskrypcji. I tu właśnie z pomocą przychodzi operator share. Share zwraca nowy strumień, które współdzieli oryginalny strumień wejściowy. Strumień będzie współdzielony tak długo jak:

  • liczba subskrybentów będzie większa od 0
  • observable nie są „completed”

Jeśli spełnione są powyższe warunki, to strumień z operatorem share jest hot.

Zastosujmy zatem operator share i sprawdźmy, czy log wywoła się tylko raz:

Faktycznie, dzięki dodaniu operatora share:

Nasz side-effect w postaci loga w operatorze tap, wywołał się wyłącznie raz.

Share i kod synchroniczny

Wyżej wspomniałem, że share będzie działał, tak długo jak subskrypcje nie są „completed”. Nasz log wywołał się raz, ponieważ nasz kod jest asynchroniczny. W momencie drugiego subscribe(), który odpalił się synchronicznie po pierwszym subscribe(), to pierwsza subskrypcja nie była jeszcze skompletowana (http.get nieco trwał).

Do czego dążę – jeśli Twój kod będzie w pełni synchroniczny, to share() nie zadziała w takiej postaci w jakiej byś przywidywał. Rozpatrzmy przykład:

 

Zamiast strzała do API, zastosowałem funkcję of, który zwraca mi observable synchronicznie. W momencie drugiej subskrypcji, pierwsza już się skompletowała (cały kod jest synchroniczny) i nie jest już zapewniony warunek, że jakakolwiek subskrypcja nie jest jeszcze „completed”. Stąd log z tap pojawił się dwa razy. W takiej sytuacji, musiałbym użyć operatora shareReplay() zamiast share(), aby log pojawił się wyłącznie raz.

Realne zastosowanie

W pracy korzystam z operatora share najczęściej w sytuacjach, gdzie na jednym widoku korzystam z wielu słowników, które dostarczają mi z backendu wartości np. do selectów w formularzach. Wygląda to mniej więcej tak:

Następnie pokemons$, heroes$ i cats$ lądują w templatce wraz z async pipe. Dzięki temu, że share() zmienia observable na hot, to robię wyłącznie jeden request do backendu. Gdybym usunął share(), to każda subskrypcja do pokemons$, heroes$ i cats$, spodowałaby nowy strzał do serwera, a tego bym nie chciał :). Można by również this.dictionaries z powyższego przykładu od razu używać w templatce za pomocą *ngIf & as, a następnie siegać po słowniki po odpowiednich kluczach już w  templatce, ale to zależy od naszych preferencji.
Możesz sobie to sprawdzić w pierwszym przykładzie z artykułu w zakładce network w web toolsach, zrób 8x subscribe do user$ to przekonasz się, że poszło 8 zapytań do serwera,  a po użyciu share, mimo 8 subskrypcji, byłby tylko jeden strzał do BE.
Tyle na dzisiaj o RxJS, stay tuned!

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *