W zeszłym tygodniu odpowiedziałem na pytanie RxJS, w którym wdałem się w dyskusję z innym członkiem społeczności na temat: „Czy powinienem utworzyć subskrypcję dla każdego konkretnego efektu ubocznego, czy powinienem ogólnie starać się minimalizować subskrypcje?” Chcę wiedzieć, jakiej metodologii należy użyć w kontekście podejścia opartego na pełnej reaktywności aplikacji lub kiedy przełączać się między nimi. Pomoże mi to, a może innym, uniknąć niepotrzebnych dyskusji.
Informacje o konfiguracji
- Wszystkie przykłady są w TypeScript
- Aby lepiej skupić się na pytaniach, nie trzeba używać cykli życia / konstruktorów do subskrypcji i zachować niepowiązane ramy
- Wyobraź sobie: subskrypcje są dodawane w init konstruktora / cyklu życia
- Wyobraź sobie: Rezygnacja z subskrypcji odbywa się w trakcie niszczenia cyklu życia
Co to jest efekt uboczny (próba kątowa)
- Aktualizacja / wejście w interfejsie użytkownika (np.
value$ | async
) - Wyjście / upstream komponentu (np.
@Output event = event$
) - Interakcja między różnymi usługami w różnych hierarchiach
Przykładowy przypadek użycia:
- Dwie funkcje:
foo: () => void; bar: (arg: any) => void
- Dwie obserwowalne źródła:
http$: Observable<any>; click$: Observable<void>
foo
jest wywoływany pohttp$
emisji i nie potrzebuje żadnej wartościbar
jest wywoływany poclick$
emisji, ale potrzebuje bieżącej wartościhttp$
Przypadek: utwórz subskrypcję dla każdego określonego efektu ubocznego
const foo$ = http$.pipe(
mapTo(void 0)
);
const bar$ = http$.pipe(
switchMap(httpValue => click$.pipe(
mapTo(httpValue)
)
);
foo$.subscribe(foo);
bar$.subscribe(bar);
Przypadek: ogólnie minimalizuj subskrypcje
http$.pipe(
tap(() => foo()),
switchMap(httpValue => click$.pipe(
mapTo(httpValue )
)
).subscribe(bar);
Krótko o mojej opinii
Rozumiem fakt, że subskrypcje na początku sprawiają, że krajobrazy Rx są bardziej złożone, ponieważ musisz na przykład pomyśleć o tym, w jaki sposób subskrybenci powinni wpływać na potok, czy nie (na przykład udostępniaj swoje obserwowalne lub nie). Ale im bardziej oddzielasz kod (tym bardziej się skupiasz: co się dzieje, kiedy), tym łatwiej jest utrzymać (testować, debugować, aktualizować) kod w przyszłości. Mając to na uwadze, zawsze tworzę pojedyncze obserwowalne źródło i pojedynczą subskrypcję dla dowolnego efektu ubocznego w moim kodzie. Jeśli dwa lub więcej efektów ubocznych, które mam, są wywoływane przez dokładnie to samo obserwowalne źródło, dzielę się moim obserwowalnym i subskrybuję każdy efekt uboczny indywidualnie, ponieważ może on mieć różne cykle życia.
źródło
takeUntil
w połączeniu z funkcją wywoływaną zngOnDestroy
. Jest to jeden liner że dodaje to do rury:takeUntil(componentDestroyed(this))
. stackoverflow.com/a/60223749/5367916jeśli optymalizowanie subskrypcji to Twoja gra końcowa, dlaczego nie przejść do logicznej ekstremum i po prostu zastosować się do tego ogólnego wzorca:
Wyłączne wykonywanie efektów ubocznych za pomocą stuknięcia i aktywacji za pomocą scalania oznacza, że masz tylko jedną subskrypcję.
Jednym z powodów, aby tego nie robić, jest kastrowanie tego, co czyni RxJS użytecznym. Jest to zdolność do komponowania obserwowalnych strumieni oraz subskrybowania / anulowania subskrypcji strumieni zgodnie z wymaganiami.
Twierdziłbym, że twoje obserwowalne powinny być logicznie skomponowane, a nie zanieczyszczone lub pomieszane w imię ograniczania subskrypcji. Czy efekt foo powinien być logicznie sprzężony z efektem paska? Czy jedno wymaga drugiego? Czy kiedykolwiek nie będę chciał uruchamiać foo, gdy emituje http $? Czy tworzę niepotrzebne sprzężenie między niepowiązanymi funkcjami? Są to wszystkie powody, dla których nie należy umieszczać ich w jednym strumieniu.
To wszystko nawet nie uwzględnia obsługi błędów, która jest łatwiejsza do zarządzania dzięki wielu subskrypcjom IMO
źródło