Dlaczego warto używać Redux-Observable zamiast Redux-Saga?

136

Użyłem Redux-Saga . Kod napisany za jego pomocą jest jak dotąd łatwy do uzasadnienia, poza tym, że funkcja generatora JS od czasu do czasu psuje mi głowę. Z mojego zrozumienia, Redux-Observable może osiągnąć podobną pracę, która obsługuje efekty uboczne, ale bez używania funkcji generatora.

Jednak dokumenty z Redux-Observable nie zawierają wielu opinii na temat tego, dlaczego jest lepszy od Redux-Saga. Chciałbym wiedzieć, czy nie korzystanie z funkcji generatora jest jedyną korzyścią z używania Redux-Observable. A jakie mogą być wady, problemy lub kompromisy wynikające z używania Redux-Observable zamiast Redux-Saga? Z góry dziękuję.

Ivan Wang
źródło
1
Zrobiłem zabawny, ale szczegółowy blog, na którym znalazłem Redux-Saga lepszą od Redux-Observable dla ludzi, którzy nie żyją / jedzą / oddychają obserwowalnymi przez cały dzień. Jestem pewien, że to świetnie, jeśli cały twój stack jest obserwowalny. shift.infinite.red/…
Gant Laborde

Odpowiedzi:

244

Zastrzeżenie: Jestem jednym z autorów książki redux-obserwowalnej, więc trudno mi być w 100% bezstronnym.

Obecnie nie podajemy żadnego powodu, dla którego obserwowalna redukcja jest lepsza niż saga redux, ponieważ ... tak nie jest. 😆

tl; dr są wady i zalety obu. Dla wielu jeden jest bardziej intuicyjny niż drugi, ale oba są trudne do nauczenia się na różne sposoby, jeśli nie znasz RxJS (obserwowalny redux) lub generatorów / „efektów jako danych” (saga redux).

Rozwiązują ten sam problem w bardzo podobny sposób, ale mają pewne fundamentalne różnice, które stają się naprawdę widoczne dopiero, gdy użyjesz ich wystarczająco dużo.

obserwowalny redux odkłada prawie wszystko na idiomatyczny RxJS. Więc jeśli masz wiedzę RxJS (lub ją zdobywasz), uczenie się i używanie redux-obserwowalne jest super naturalne. Oznacza to również, że tę wiedzę można przenieść na inne cele niż redukx. Jeśli zdecydujesz się przejść na MobX, jeśli zdecydujesz się przejść na Angular2, jeśli zdecydujesz się przejść na jakąś przyszłą popularność X, są bardzo duże szanse, że RxJS może ci pomóc. Dzieje się tak, ponieważ RxJS jest ogólną biblioteką asynchroniczną i pod wieloma względami przypomina sam język programowania - cały paradygmat „programowania reaktywnego”. RxJS istniał od 2012 roku i zaczynał jako port Rx.NET („porty” są dostępne w prawie każdym głównym języku, jest to przydatne ).

redux-saga zapewnia swoje operatory oparte na czasie, więc chociaż wiedza, którą zdobędziesz na temat generatorów i radzenia sobie z efektami ubocznymi w tym stylu menedżera procesów, jest przenoszalna, rzeczywiste operatory i użycie nie jest używane w żadnej innej dużej bibliotece. To trochę niefortunne, ale z pewnością nie powinno samo w sobie przełamać umowy.

Używa również "efektów jako danych" ( opisanych tutaj ), co może być trudne na początku, ale oznacza to, że kod sagi redux w rzeczywistości sam nie wykonuje efektów ubocznych. Zamiast tego funkcje pomocnicze, których używasz, tworzą obiekty, które są jak zadania, które reprezentują zamiar wywołania efektu ubocznego, a następnie wewnętrzna biblioteka wykonuje go za Ciebie. To sprawia, że ​​testowanie jest niezwykle łatwe, bez potrzeby kpiny i jest bardzo atrakcyjne dla niektórych osób. Jednak osobiście odkryłem, że oznacza to, że twoje testy jednostkowe ponownie zaimplementują większość logiki twojej sagi - co sprawia, że ​​te testy nie są zbyt przydatne IMO (ta opinia nie jest podzielana przez wszystkich)

Ludzie często pytają, dlaczego nie robimy czegoś takiego z obserwowalnymi reduksami: według mnie jest to zasadniczo niekompatybilne z normalnym idiomatycznym Rx. W Rx używamy takich operatorów, .debounceTime()które hermetyzują logikę wymaganą do odbicia, ale to oznacza, że ​​jeśli chcieliśmy stworzyć wersję, która nie wykonuje odbicia, a zamiast tego emituje obiekty zadań z zamiarem, straciłeś teraz moc Rx, ponieważ nie można już po prostu łączyć operatorów, ponieważ operowaliby na tym obiekcie zadania, a nie na rzeczywistym wyniku operacji. Naprawdę trudno to elegancko wyjaśnić. Znowu wymaga głębokiego zrozumienia Rx, aby zrozumieć niezgodność podejść. Jeśli naprawdę chcesz czegoś takiego, sprawdź cykle redukcyjnektóry korzysta z cycle.js i głównie ma te cele. Wydaje mi się, że wymaga to zbyt wiele ceremonii jak na mój gust, ale zachęcam do zakręcenia, jeśli Cię to interesuje.

Jak wspomniał ThorbenA, nie cofam się przed przyznaniem, że Redux-saga jest obecnie (13.10.16) wyraźnym liderem w złożonym zarządzaniu skutkami ubocznymi reduxu. Został uruchomiony wcześniej i ma bardziej solidną społeczność. Tak więc używanie de facto standardu w stosunku do nowego dzieciaka z bloku jest bardzo atrakcyjne. Myślę, że można bezpiecznie powiedzieć, że jeśli używasz któregokolwiek z nich bez wcześniejszej wiedzy, możesz się trochę pomylić. Obaj używamy dość zaawansowanych koncepcji, które, gdy już się „dostaniesz”, znacznie ułatwiają zarządzanie złożonymi skutkami ubocznymi, ale do tego czasu wielu się potyka.

Najważniejszą radą, jakiej mogę udzielić, jest to, aby nie sprowadzać żadnej z tych bibliotek, zanim będziesz ich potrzebować. Jeśli wykonujesz tylko proste wywołania Ajax, prawdopodobnie ich nie potrzebujesz. redux-thunk jest głupi, łatwy do nauczenia i zapewnia wystarczającą ilość podstaw - ale im bardziej złożona asynchronizacja, tym trudniejsza (lub nawet niemożliwa) staje się dla redux-thunk. Ale w przypadku sagi obserwowalnej przez reduksa pod wieloma względami świeci ona najbardziej, im bardziej złożona jest asynchronizacja. Jest też wiele zalet używania Redux-thunk z jednym z pozostałych (redukx-obserowalne / saga) w tym samym projekcie! redux-thunk dla zwykłych prostych rzeczy, a następnie używając tylko redux-observable / saga do złożonych rzeczy. To świetny sposób na utrzymanie produktywności, więc nie walczysz z obserwowalnymi / sagami reduxu o rzeczy, które byłyby trywialne w przypadku Redux-thunk.

jayphelps
źródło
3
Po prostu zobaczyłem twoją rozmowę (uuhf dźwięk!) I od razu naciśnij ⌘ + T + "redux-saga vs redux-observable". Używam sagi redux już od jakiegoś czasu (szczególnie w React Native), ale po obejrzeniu twojego wystąpienia i tego postu widzę kilka przypadków użycia (dla mnie), w których redux-obs. faktycznie byłoby lepiej dopasowane. Wasz przykład o debounceTime()„utracie” kontroli nad bardzo ogólną logiką uderzył mnie. Dzięki za wyjaśnienie.
Hulvej
3
Po prostu zobaczyłem rozmowę i trochę więcej googlowałem. Dobre rzeczy @jayphelps, dzięki za udostępnienie. Szczególnie podoba mi się twój komentarz dotyczący używania redux-thunk w połączeniu z redux-observable / saga. To ma sens, po co nadmiernie komplikować proste żądania AJAX, gdy są niepotrzebne. To powiedziawszy, jest coś do powiedzenia na temat jednolitości i zachowania spójności ludzi. Dzięki jeszcze raz!
Spets
Przed aktualizacją do redux-saga / redux-observable, możesz wypróbować redux-dispatch-listener, który jest bardzo prosty i może już rozwiązać niektóre z twoich zastosowań: github.com/slorber/redux-dispatch-subscribe
Sebastien Lorber
To była bardzo przydatna odpowiedź. Dziękuję Ci! Podoba mi się kwestia możliwości transferu wiedzy o RxJS do innych domen / frameworków.
Anselan,
@jayphelps Jaki byłby przykład „złożonej asynchronicznej”. Obecnie próbuję ocenić, czy powinienem przejść z thunk do sagi / obserables dla projektu. Dzięki :)
Sam Bokai
65

Myślę, że są rzeczy, które należy wziąć pod uwagę.

  1. Złożoność
  2. Styl kodowania
  3. Krzywa uczenia się
  4. Testowalność

Powiedzmy, że chcemy pobrać użytkownika z API

// Redux-Saga

import axios from 'axios' 

function* watchSaga(){
  yield takeEvery('fetch_user', fetchUser) // waiting for action (fetch_user)
}

function* fetchUser(action){
    try {
        yield put({type:'fetch_user_ing'})
        const response = yield call(axios.get,'/api/users/1')
        yield put({type:'fetch_user_done',user:response.data})
  } catch (error) {
        yield put({type:'fetch_user_error',error})
  }
}

// Redux-Observable
import axios from 'axios'

const fetchUserEpic = action$ => 
    action$
        .ofType('fetch_user')
        .flatMap(()=>
          Observable.from(axios.get('/api/users/1')) // or use Observable.ajax
            .map(response=>({type:'fetch_user_done', user:response.data}))
            .catch(error => Observable.of({type:'fetch_user_error',error}))
            .startWith({type:'fetch_user_ing'})
        )

Napisałem również ten artykuł w celu dokładnego porównania różnic między sagą Redux i Redux-Observable. Sprawdź ten link tutaj lub prezentację .

Wayne Chiu
źródło
3
to porównanie z linkiem jest świetne, dzięki
rofrol
1
Uwielbiam to porównanie, ALE jest z nim problem, który chcę poruszyć. Porównując je za pomocą wywołań API - używasz funkcji fetch, aby można było zaobserwować redux. fajne. ALE, kiedy pokazujesz "anulowalne" różnice ... NIE używasz pobierania - zamiast tego używasz wewnętrznego Observable.ajax ... dlaczego? Wolałbym zachować to przy użyciu „fetch” lub „axios”. w przeciwnym razie świetna robota.
james emanon
5
@jamesemanon Zakładam, że nie używa pobierania, ponieważ API pobierania nie ma jeszcze opcji anulowania. (więcej na ten temat: github.com/whatwg/fetch/issues/27 )
Daniel Andrei
Wow, to dogłębne porównanie ze wszystkimi przykładami jest najlepsze. Dziękuję Ci!
Radek Matěj
24

Używam Redux-Observable zamiast Redux-Saga, ponieważ wolę pracować z obserwowalnymi niż generatorami. Używam go z RXJS, która jest potężną biblioteką do pracy ze strumieniami danych. Pomyśl o tym jak o lodash dla async. Jeśli chodzi o wszelkie wady, problemy i kompromisy w wyborze jednego z nich, spójrz na tę odpowiedź Jaya Phelpsa:

Saga Redux jako projekt istnieje dłużej niż możliwy do zaobserwowania, więc jest to z pewnością jeden z głównych punktów sprzedaży. Znajdziesz więcej dokumentacji, przykładów i prawdopodobnie będziesz mieć lepszą społeczność, od której możesz uzyskać wsparcie.

Przeciwnik polega na tym, że operatorzy i interfejsy API, których uczysz się w sadze redux, nie są tak przenoszone jak nauka RxJS, która jest używana wszędzie. Obserwowalne przez redux jest super super super proste wewnętrznie, po prostu daje ci naturalny sposób używania RxJS. Więc jeśli znasz RxJS (lub chcesz), jest to niezwykle naturalne dopasowanie.

Moja rada w tej chwili dla większości ludzi jest taka, że ​​jeśli musisz zapytać, której z nich użyć, prawdopodobnie powinieneś wybrać redux-saga.

ThorbenA
źródło
11

Redux-Observable to niesamowita biblioteka, używamy jej w produkcji przez 1,5 roku bez żadnych problemów do tej pory, jest doskonale testowalna i można ją łatwo zintegrować z dowolnym frameworkiem. Mamy ekstremalnie przeciążone równoległe kanały gniazd, a jedyną rzeczą, która chroni nas przed zawieszeniami, jest obserwowalna redux

Chciałbym tu wspomnieć o 3 punktach.

1. Złożoność i krzywa uczenia się

Saga Redux z łatwością bije tutaj redukcję, którą można zaobserwować. Jeśli potrzebujesz tylko prostego wniosku, aby uzyskać autoryzację, a nie chcesz używać redux-thunk z jakichś powodów, powinieneś rozważyć użycie redux-saga, jest to po prostu łatwiejsze do zrozumienia.

Jeśli nie masz wcześniejszej wiedzy na temat Observable, będzie to dla ciebie uciążliwe, a twój zespół cię oprowadzi :)

2. Co może mi zaoferować Observable i RxJS?

Jeśli chodzi o logikę asynchroniczną, Observable to twój szwajcarski nóż, Observable może zrobić za Ciebie dosłownie wszystko. Nigdy nie powinieneś porównywać ich z obietnicami lub generatorami, który jest znacznie mocniejszy, to tak samo, jak porównanie Optimusa Prime z Chevroletem.

A co z RxJS? To jest jak lodash.js, ale dla logiki asynchronicznej, kiedy już wejdziesz, nigdy nie przełączysz się na coś innego.

3. Rozszerzenie reaktywne

Po prostu sprawdź ten link

http://reactivex.io/languages.html

Rozszerzenie reaktywne jest zaimplementowane dla wszystkich nowoczesnych języków programowania, to tylko Twój klucz do programowania funkcjonalnego.

Spędź więc czas mądrze, ucz się RxJS i używaj redux-obserwa- calne

Denis Rybalka
źródło
8

Cenię możliwość przenoszenia między językami i środowiskami wykonawczymi, które ma Rx. Nawet jeśli Twoja aplikacja nie zmieni języka, Twoja kariera może. Zdobądź najlepszą możliwą dźwignię w nauce, bez względu na to, jak oceniasz to dla siebie. W szczególności jest to świetna brama do .Net LINQ.

Dean Radcliffe
źródło
2
Mądry wybór, chociaż generatory są również niezależne od języka.
Greg Herbowicz
4

Ponieważ jest tu cała masa rozmów, które można zaobserwować w reduksie, pomyślałem, że przedstawię sagę argumentacji. Nie używam obserwowalnych reduxów ani RxJS, więc nie mogę dać porównania obok siebie, ale użyłem sagi z doskonałym efektem.

Na ile to warte, używam sag w produkcji w aplikacji internetowej.

Sagas kontra Thunk

Saga wygrywa. Nie podobało mi się, jak Thunk umieścił logikę w moich twórcach akcji. Sprawiało to również, że wykonanie kilku żądań z rzędu było kłopotliwe. Krótko przyjrzałem się temu, co można zaobserwować w tej pracy, ale zdecydowałem się na Sagas.

Krzywa uczenia się dla sag

Zrozumienie, czym są generatory i dlaczego są ważne, jest kluczem do zrozumienia sag. Ale podkreślę, że nie musisz znać generatorów od podszewki. Musisz tylko wiedzieć, że przekazujesz kontrolę za pomocą instrukcji yield i że saga przekaże kontrolę z powrotem po rozwiązaniu kodu asynchronicznego. Po tym kawałku nietrudno jest zrozumieć, co dzieje się w sadze.

Podstawowe metody sagi to (z mojego doświadczenia):

  • call- Wywołaj dowolny fragment kodu i uzyskaj wartość zwracaną. Obsługuje obietnice. Świetna synergia między przetwarzaniem asynchronicznym i sagami.
  • select- Zadzwoń do selektora. Ten kawałek jest raczej genialny. Selektory są kluczowe dla Redux i są w 100% obsługiwane!
  • put- aka dispatchakcja. W rzeczywistości wyślij tyle, ile chcesz!

Są inne funkcje, ale jeśli opanujesz te trzy, będziesz w naprawdę dobrym miejscu.

Wniosek

Powodem, dla którego wybrałem sagi, była łatwość obsługi. obserwowalne w reduksie wyglądało na wyzwanie. Jestem w 100% zadowolony z sag. Bardziej szczęśliwy, niż się spodziewałem.

Z mojego doświadczenia wynika, że ​​Sagi są (o wiele) lepsze niż bzdury i stosunkowo łatwe do zrozumienia. Rx nie jest filiżanką herbaty dla każdego. Zdecydowanie rozważałbym sagi zamiast obserwowalnych reduxów, jeśli nie pochodzisz z tego ekosystemu i / lub nie planujesz używać Rx w przyszłości.

Julius Ecker
źródło
3

Jeśli piszesz swoją aplikację w Typescript, polecam sprawdzenie bez typu . Jest inspirowany Redux-Observable, a także zależy od RxJS, ale istnieje cały ekosystem do tworzenia aplikacji.

Największą wadą sagi, którą można zaobserwować / zredukować, jest brak wytycznych. Nie ma oficjalnych wytycznych dotyczących leniwego zmniejszania obciążenia, sag lub eposów. Podział kodu jest krytyczny podczas skalowania większych aplikacji. Niestandardowe rozwiązania do leniwego ładowania zwykle nie działają z HMR, co powoduje złe doświadczenia programistów.

Beztypowe plusy:

  1. Zaprojektowane dla języka TypeScript
    Wszystkie interfejsy API są zaprojektowane pod kątem maszynopisu i bezpieczeństwa typów:
    • Maszynopis zwiększy Twoją produktywność, a nie zwolni.
    • Wymagane są tylko niezbędne adnotacje: stan, argumenty akcji.
    • Bez typowania. Wszystko jest wywnioskowane automatycznie. 95% kodu wygląda jak czysty javascript.
    • Brak RootAction, RootEpic, RootState ani innych typów pomocniczych.
  2. Zapewnij wszystkie bloki konstrukcyjne
    • Typeless obejmuje wszystko, co potrzebne do tworzenia aplikacji średniej wielkości lub klasy korporacyjnej.
    • Nie musisz polegać na wielu małych bibliotekach.
  3. Modułowość
    • Właściwa modułowość ma kluczowe znaczenie przy tworzeniu skalowalnych aplikacji.
    • Nie ma potrzeby tworzenia plików root dla epików, reduktorów, typów itp. Po utworzeniu nowego modułu możesz go dołączyć z dowolnego miejsca. Podobny do standardowych komponentów React.
  4. Uparty
    • Wszystkie typowe przypadki użycia i problemy są domyślnie rozwiązywane. Nie ma potrzeby zastanawiania się, jak rozwiązać trywialne problemy.
    • Przedstawiamy wszystkie zalecenia i najlepsze praktyki!

Sprawdź https://typeless.js.org/

niebo
źródło
2
Zalecając oprogramowanie, którego jesteś głównym autorem, należy dodać wyłączenie odpowiedzialności.
Hagelt18