Why use Redux-Observable over Redux-Saga?
Asked Answered
N

6

150

I have used Redux-Saga. Code written with it is easy to reason so far, except JS generator function is messing up my head from time to time. From my understanding, Redux-Observable can achieve the similar job that handles side effects but without using generator function.

However, docs from Redux-Observable does not provide many opinions of why it is superior against Redux-Saga. I would want to know whether not using generator function is the only benefit of using Redux-Observable. And what could be the downsides, gotcha or compromises from using Redux-Observable instead of Redux-Saga?

Namangan answered 13/10, 2016 at 12:42 Comment(1)
I had done a fun but detailed blog where I found Redux-Saga superior to Redux-Observable for people who don't live/eat/breathe observables all day. I'm sure it's great if your whole stack is observables. shift.infinite.red/… – Sclaff
C
268

Disclaimer: I am one of the authors of redux-observable so it's hard for me to be 100% impartial.

We don't currently provide any reason redux-observable is better than redux-saga because...it's not. πŸ˜†

tl;dr there are pros and cons to both. Many will find one more intuitive than the other, but both are complex to learn in different ways if you don't know RxJS (redux-observable) or generators/"effects as data" (redux-saga).

They solve the same problem in extremely similar ways, but have some fundamental differences that only become truly apparent once you use them enough.

redux-observable defers nearly everything to idiomatic RxJS. So if you have RxJS knowledge (or gain it), learning and using redux-observable is super super natural. That also means that this knowledge is transferable to things other than redux. If you decide to switch to MobX, if you decide to switch to Angular2, if you decide to switch to some future hotness X, chances are extremely good that RxJS can help you. This is because RxJS is a generic async library, and in many ways is like a programming language in itselfβ€”the whole "Reactive Programming" paradigm. RxJS existed since 2012 and started as a port of Rx.NET (there are "ports" in nearly every major language, it's that useful).

redux-saga provides its time-based operators itself, so while the knowledge you acquire about generators and handling side effects in this process-manager style is transferable, the actual operators and usage is not used in any other major library. So that's a little unfortunate, but certainly shouldn't be a deal-breaker by itself.

It also uses "effects as data" (described here), which might be hard to wrap your head around at first, but it means that your redux-saga code doesn't actually perform the side effects itself. Instead, the helper functions you use create objects that are like tasks that represent the intent to do the side effect and then the internal library performs it for you. This makes testing extremely easy, without the need for mocking and is very attractive to some people. However, I personally have found it means your unit tests reimplement much of your saga's logic--making those tests not very useful IMO (this opinion isn't shared by everyone)

People often ask why we don't do something like that with redux-observable: to me it's fundamentally incompatible with normal idiomatic Rx. In Rx, we use operators like .debounceTime() that encapsulates the logic required to debounce, but that means if we wanted to make a version of it that doesn't actually perform the debouncing and instead emits task objects with the intent, you've now lost the power of Rx because you can't just chain operators anymore because they'd be operating on that task object, not the real result of the operation. This is really hard to explain elegantly. It again requires heavy understanding of Rx to understand the incompatibility of approaches. If you really want something like that, check out redux-cycles which uses cycle.js and mostly has those goals. I find it requires too much ceremony for my tastes, but I encourage you to give it a spin if it interests you.

As ThorbenA mentioned, I don't shy away from admitting that redux-saga is currently (10/13/16) the clear leader in complex side effect management for redux. It was started earlier and has a more robust community. So there's a lot of attraction to using the de facto standard over the new kid on the block. I think it's safe to say if you use either without prior knowledge, you're in for some confusion. We both use fairly advanced concepts that once you "get", makes complex side effect management far easier, but until then many stumble.

The most important advice I can give is not to bring in either of these libraries before you need them. If you're only making simple ajax calls, you probably don't need them. redux-thunk is stupid simple to learn and provides enough for the basics--but the more complex the async the harder (or even impossible) it becomes for redux-thunk. But for redux-observable/saga in many ways it shines the most the more complex the async is. There's also a lot of merit in using redux-thunk with one of the others (redux-observable/saga) in the same project! redux-thunk for your common simple stuff and then only using redux-observable/saga for complex stuff. That's a great way to remain productive, so you're not fighting redux-observable/saga for things that would be trivial with redux-thunk.

Circuity answered 13/10, 2016 at 17:51 Comment(6)
Just saw your talk (uuhf the sound!), and immediately hit ⌘+T + "redux-saga vs redux-observable". I have used redux-saga for quite some time now (especially in React Native), but after have watched your talk and this post I can see some use-cases (for me) where redux-obs. would actually be a better fit. Your example about debounceTime() and having "lost" the control over a very generic logic kind of made hit it for me. Thanks for explaining. – Chasechaser
Just saw the talk as well and did a bit more googling around. Good stuff @jayphelps, thanks for sharing. I especially like your comment around using redux-thunk in conjunction with redux-observable/saga. That makes a lot of sense, why overcomplicate simple AJAX requests when its unnecessary. That said, there's something to be said for uniformity and keeping people consistent. Thanks again! – Rosalynrosalynd
Before upgrading to redux-saga/redux-observable, you can try redux-dispatch-listener and which is very simple and can already solve some of your usecases: github.com/slorber/redux-dispatch-subscribe – Starlastarlene
This was a very useful answer. Thank you! I like the point about being able to transfer knowledge of RxJS to other domains/frameworks. – Lorient
@Circuity What would be an example of "complex async". Currently trying to assess wether i should switch from thunk to saga/observables for a project. Thanks :) – Winegar
Redux-Observable readme in GitHub should include link to this answer – Finagle
F
69

I think there are things that you need to take in consideration.

  1. Complexity
  2. Coding Style
  3. Learning Curve
  4. Testability

Lets say we want to fetch user from 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'})
        )

Also, I have written this article in order compare the differences between Redux-saga and Redux-Observable in depth. Check out this link here or presentation.

Fluting answered 13/1, 2017 at 4:9 Comment(4)
this side-by-side-comparison from the link is great, thanks – Sanders
I love the comparison, BUT there is an issue with it i want to bring up. When you compare them using api calls - you are using fetch for redux-observable. cool. BUT, when you show "cancelable" differences.. you do NOT use fetch -- instead you use the internal Observable.ajax... why? I'd prefer to keep it using "fetch" or "axios". otherwise, great work there. – Eyeopener
@jamesemanon I assume he's not using fetch because fetch API doesn't have the option to cancel yet. (more on this: github.com/whatwg/fetch/issues/27) – Wearing
Wow, that in-depth comparison with all the examples is the best. Thank you! – Masorete
Q
25

I use Redux-Observable over Redux-Saga because prefer to work with observables over generators. I use it with RXJS, which is a powerful library for working with streams of data. Think of it like lodash for async. In terms of any downsides, gotcha and compromises in choosing one over the other, take a look at this answer from Jay Phelps:

redux-saga as a project has existed longer than redux-observable so that's certainly one major selling point. You'll find more documentation, examples, and likely are have a better community to get support from.

The counter being that the operators and APIs you learn in redux-saga aren't nearly as transferable as learning RxJS, which is used all over the place. redux-observable is super super super simple internally, it's really just giving you a natural way for you to use RxJS. So if you know RxJS (or want to), it's an extremely natural fit.

My advice at the moment for most people is that if you have to ask which one you should use, you probably should choose redux-saga.

Quadruple answered 13/10, 2016 at 13:35 Comment(0)
E
15

Redux-Observable is an amazing library, we use it in production for 1.5 years without any issues so far, it's perfectly testable and can be easily integrated with any framework. We are having extremely overloaded parallel socket channels and the only thing which is saving us from freezes is Redux-Observable

I have 3 points I'd like to mention here.

1. Complexity and learning curve

Redux-saga easily beats redux-observable here. If you need just a simple request to get authorization done and you don't want to use redux-thunk for some reasons, you should consider using redux-saga, it's just easier to understand.

If you don't have prior knowledge of Observable it will be a pain for you and your team will course you :)

2. What can Observable and RxJS offer to me?

When it comes to async logic Observable is your Swiss knife, Observable can literally do almost everything for you. You should never compare them to promises or generators it's far more powerful, it's same like comparing Optimus Prime with Chevrolet.

And what about RxJS? It's like lodash.js but for async logic, once you in you will never switch to something different.

3. Reactive extension

Just check this link

http://reactivex.io/languages.html

The reactive extension is implemented for all modern programming languages, it's just your key to functional programming.

So spend your time wisely learn RxJS and use redux-observable :)

Eldredge answered 25/1, 2018 at 21:37 Comment(1)
The best part of this answer: "What can Observable and RxJS offer to me?" => "It's the best! Don't compare it anything! It's Optimus Prime!" :) – Mika
H
10

I value the transferability across languages and runtimes that Rx has. Even if your app won't change languages, your career may. Get the best leverage you can on your learning, however you size that up for yourself. It's such a great gateway to .Net LINQ in particular.

Housemaster answered 20/4, 2017 at 3:11 Comment(1)
Smart choice, though generators are language-agnostic too. – Cuboid
K
6

Since there's a whole bunch of redux-observable talk in here, I'd thought I'd give the saga side of the argument. I don't use redux-observable or RxJS, so I can't give a side by side comparison, but I have used sagas to great effect.

For what its worth, I'm using sagas in production in a web application.

Sagas vs. Thunk

Saga wins hands down. I didn't like how thunk put logic in my action creators. It also made doing a few requests in a row troublesome. I briefly looked at redux-observable for this job, but settled on Sagas.

Learning Curve for Sagas

Understanding what generators are and why they're important is key to understanding sagas. But I will stress that you don't need to know generators inside and out. You only need to know that you're passing off control with the yield statement, and that the saga will pass back control after your async code resolves. After that bit, its not very hard to understand what's going on in a saga.

The core saga methods are (in my experience):

  • call - Call any bit of code and get the return value. Supports promises. Great synergy between async processing and sagas.
  • select - Call a selector. This bit is rather brilliant. Selectors are core to redux, and they are 100% supported!
  • put - aka dispatch an action. In fact dispatch as many as you want!

There are other functions, but if you can master those three, you will be in a really good spot.

Conclusion

The reason I chose sagas was the ease of use. redux-observable looked like a challenge. I am 100% satisfied with sagas. More happy than I ever expected.

In my experience, Sagas are (way) better than thunks and relatively easy to understand. Rx is not everyone's cup of tea. I would strongly consider sagas instead of redux-observable if you don't come from that ecosystem and/or don't plan on using Rx in the future.

Kyla answered 5/2, 2019 at 19:19 Comment(1)
Although not relative to the Q but good point. – Footlights

© 2022 - 2024 β€” McMap. All rights reserved.