
Observables, Side-Effects, and Subscriptions: Some Reactive Best Practices - Eyas
https://blog.eyas.sh/2018/12/observables-side-effects-and-subscriptions/
======
vikingcaffiene
At my job we went from using observables for everything to literally
questioning every single one that shows up in a PR. Most of the time they
don't need to be there. Several spots in our app are nigh unusable/untestable
messes because of the side effects that get caused from subscriptions. Eg, you
make a change in one place and something else totally unrelated gets updated.
Obviously the implementation was totally wrong there but I tend to believe in
the idea of a "pit of success" with framework api's. With observables, it's a
giant foot gun if you don't understand what you are doing and you are better
off using something more stateless like a promise or just a simple javascript
object reference shared via singleton service or something. If you aren't
careful about managing your "source of truth" you are gonna have a bad time.
Then of course theres remembering to unsubscribe from them... memory leak
anyone?

While I think they are a powerful tool, I find that a lot of developers get
them confused as a drop in replacement for promises and do things like nest
subscriptions etc. For most standard CRUD apps, either do as the author
suggests, and use an async pipe in your template, or, convert them to promises
as soon as possible.

What are they good for? Streams of data. Web Sockets, iframe post messages,
event handlers for instance. Anything that is actually real time and makes
changes without deliberate action.

~~~
Udik
Can you give some non-trivial and not excessively contrived example of when
observables should be used? I'm genuinely asking because, while I find them
elegant, I still can't figure out why they were put front-and-center of the
current generation of web frameworks (especially Angular). They seem a good
tool to have but mostly for very particular cases.

~~~
maxbendick
I've had a lot of success using observables for side-effects in Redux and
NGRX. It's flexible, testable, and reliable.

I can map, filter, etc. my app's actions to perform any side-effect I want,
possibly sending a success or failure action on completion. I can even listen
for changes in the store or other streams to dispatch actions.

You can still do everything you need with Thunk, Sagas, etc., but I enjoy the
ease and flexibility that RxJS gives me.

Libraries that help with this pattern:

Redux Observables: [https://redux-observable.js.org/](https://redux-
observable.js.org/)

NGRX Effects: [https://ngrx.io/guide/effects](https://ngrx.io/guide/effects)

------
dmitriid
There’s one thing I find weird. There are very few, if any, resources that go
beyond the simple “set up an observable, run through at most two
transformations and at most two subscriptions”.

That’s where the vast majority of tutorials, blog posts and presentations end.
And then you’re stuck with trying to implement a simple login flow, or reading
data from a file, and... nothing really works, everything is overengineered
and impossible to debug and trace.

In his book the _author of RxJava_ openly admits it took him _several months_
to grok reactive streams. And he was taught _by the author of reactive
extensions for dotnet_.

Programming with reactive stuff is essentially async programming (never easy)
where your data is handled, transformed in async blackboxes (hardly any
implementation of Rx has a good way to observe, debug and trace data through
them), and is delivered to sinks/subscribers in async manner.

And yet basically everything you find on the topic will cheerfully tell you
how easy this stuff is, and present you with a toy code that fits on a screen.

~~~
arenaninja
I've been using Observables pretty exclusively for almost two years now. What
I have now is a cookbook of use cases that I refer back to by looking at what
I did in previous projects. That's not to say there's no original thought in
new code that I write, just that sometimes the nontrivial cases have escaped
my memory. Some examples:

* How do you end the observable's subscription when any of X, Y or Z happen (and at least one of these is a timer)?

* How do you emit N network requests in series, without nesting subscriptions? (ideally you won't need to, but not all APIs are well behaved)

* How do you transform a node-like stream to an observable?

This is just from last week, and some I'm able to do from scratch better than
others.

TESTING them is challenging as well. There are non-obvious failures when you
use the typical unit testing patterns (a testing framework will report that
function was/wasn't called and you're not sure why, because per the test setup
that shouldn't be what happened). Almost two years in and I have no idea what
the marbles mean, but soon I'll start trying to grok them

But there's no way I see how I can do my job without them. The applications I
work on take updates from 1) keyboard 2) mouse 3) direct websocket
subscriptions 4) indirect websocket subscriptions 5) xhr calls 6) other
messaging patterns that aren't as common.

So I'm at the point where I can easily tell you the differences between
switchMap, concatMap, and mergeMap (of which flatMap is an alias), easily pull
out filter, map, combineLatest, tap, etc. Sometimes when I'm in the weeds it
does help to think that they're just functions, but at no point would I dare
tell anyone that this stuff is easy. It's easily the hardest thing I've
learned since I started my current job.

------
stupidcar
It's ironic that the author's recommendation of using `shareReplay(1)`
actually leaks a subscription as well. Unless the operator is a supplied with
`{ refCount: true }` config parameter, it does _not_ unsubscribe from the
source observable, even when all its subscribers unsubscribe:
[https://github.com/ReactiveX/rxjs/blob/master/src/internal/o...](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/shareReplay.ts#L114)

Sadly, issues like this seem to crop up all the time when using reactive
programming. I'm not as anti-reactive as most other commenters here seem to
be, but there's no doubt that certain areas, particularly memory safety,
sharing and buffering, seem hard to understand and get right.

~~~
Eyas
My bad. The recommendation at Google is to use publishReplay + refCount
precisely for that reason, but surprisingly there's so little public
documentation on e.g. the publishReplay rxjs operator, that I decided to go
with what was ultimately more prevalent online.

Probably worth fixing that though, and creating those resources out there.

------
polyterative
After over two years heavily using RxJS in Angular for all kids of stuff (I
once mapped a 100+ step-long Excel formula with success), the only advice I
can give is to make a single pipe for a single purpose, using map() and
friends to run data down the pipe and performing the decisive final action in
the subscribe, possibily a one-line action.

Doing this reduces risk of side effects and help reading the code/debugging.

Also you must take in account stuff like back-pressure and multicasting
observables, which are as useful as hard to master and maintain correctly.

Really once you stop putting subscriptions in subscriptions by using the
switchMap or switchMapTo operators, a lot of errors start to become clear and
avoided.

------
cphoover
When the hell did we start calling streams "observables"?

~~~
Eyas
They're not quite the same thing. Here's a post that goes over some of the
differences:

[https://medium.com/@rakia/promise-vs-observable-vs-
stream-16...](https://medium.com/@rakia/promise-vs-observable-vs-
stream-165a310e886f#b917)

The big difference is the pull-vs-push scheme, making Observables lazy-by-
default, and just subscribed vs unsubscribed to.

Observable is a theoretical concept in reactive programming, popularized by
ReactiveX author Eric Meijer.

It's gained quite a bit of popularity because ReactiveX is heavily used in
Java frontends and JavaScript (Angular especially).

~~~
mpweiher
That article seems to take a very specific stream implementation as the basis
for those purported differences.

There are many others, and then the differences go away.

In fact, Rx can be traced directly to the synchronous dataflow[1] of Lustre[2]
and Esterel[3], which in turn goes back to Lucid[4], "The Dataflow Programming
Language"[5].

It took a bit to ferret this out of the publication record, but when I asked
Erik he confirmed.

[1]
[https://ptolemy.berkeley.edu/publications/papers/87/synchdat...](https://ptolemy.berkeley.edu/publications/papers/87/synchdataflow/)

[2]
[https://en.wikipedia.org/wiki/Lustre_(programming_language)](https://en.wikipedia.org/wiki/Lustre_\(programming_language\))

[3]
[https://en.wikipedia.org/wiki/Esterel](https://en.wikipedia.org/wiki/Esterel)

[4]
[https://en.wikipedia.org/wiki/Lucid_(programming_language)](https://en.wikipedia.org/wiki/Lucid_\(programming_language\))

[5]
[http://www.cse.unsw.edu.au/~plaice/archive/WWW/1985/B-AP85-L...](http://www.cse.unsw.edu.au/~plaice/archive/WWW/1985/B-AP85-LucidDataflow.pdf)

~~~
Eyas
Right-- I don't disagree. But I do think 'Observable' is precise here: some
streams are lazy, some streams involve pull rather than push, but if someone
says "Stream" as a datatype, you don't necessarily know what they mean.

But sure enough, if all we care about is a "collection of observed values,
asynchronously" 'Stream' and 'Observable' fit the bill in all(most?)
implementations.

~~~
mpweiher
Yeah, 'Observable' is an artefact of a specific dataflow/streaming
implementation.

------
dmitryminkovsky
I really love this implementation:
[https://github.com/facebook/relay/blob/master/packages/relay...](https://github.com/facebook/relay/blob/master/packages/relay-
runtime/network/RelayObservable.js)

It’s one of those really nice pieces of code that’s just small enough to get
your head around but at the same time totally real and useful.

