
RxJS is great. So why have I moved on? - swannodette
https://medium.com/@puppybits/rxjs-is-great-so-why-have-i-moved-on-534c513e7af3#.h482688wk
======
disantlor
Discovering and mastering bacon.js/FRP completely changed the way I think
about programming. It hugely increased the quality and stability of my code in
ways that continue to surprise and amaze me. I cannot recommend it enough, but
this article definitely has me curious about ClojureScript.

In the meantime, the biggest issue I've had with bacon is figuring out what
are the best practices in organizing the code. One does end up with a lot of
streams or variations/combinations on streams used for different purposes.
Eventually it does become hard to follow, as the author mentions, though
because all state is self-contained you can change any one part with a high
degree of confidence that it won't break other features.

Still there must be some ideas out there that don't involve incorporating a
full blown framework/SPA architecture. The author of bacon has a blog post on
the subject but it seemed to me that there could be better ways still. Anyone
have any suggestions?

~~~
grayrest
I wrote a 20k LoC bacon app in 2014 and had the same issue with being able to
understand all the pieces individually but having trouble comprehending the
whole system.

I don't have non-architecture answers for how to organize code since that's
pretty much the point of an architecture. Most of the application organization
patterns around the React space are only ~200 lines of code and a description
of how the pieces fit together. You don't have to adopt the whole thing
immediately but you don't get the full benefits of a pattern until everything
works the same way.

For my part, I got a job writing clojurescript full time and migrated the app
to re-frame [1][2] this year in pieces over the course of a couple months and
it's the first frontend architecture that's made me happy in over a decade of
continuous searching. The js version is Redux but I think Redux is missing
middleware as a concept.

[1] [https://github.com/Day8/re-frame/](https://github.com/Day8/re-frame/) [2]
[https://github.com/binaryage/pure-frame](https://github.com/binaryage/pure-
frame) <\- Fork with no global atom

What this provides is a complete event cycle of completely pure functions (the
state swap happens in the framework) with all the data in an immutable map in
a single atom. By namespacing my subscriptions and events I can see a problem,
look at the component, and know immediately where my problem is within ~10
lines of code. It's not perfect but it's pretty good.

One thing that doesn't get mentioned often enough when talking about
Clojurescript is the potential for doing server-side rendering of the app
without having to run node. [3]

[3] [http://yogthos.net/posts/2015-11-24-Serverside-
Reagent.html](http://yogthos.net/posts/2015-11-24-Serverside-Reagent.html)

~~~
mercurial
> The js version is Redux but I think Redux is missing middleware as a
> concept.

Redux totally has middleware (you actually need middleware for async actions).

~~~
grayrest
Happy to hear that. I'm not actively using it and I find the the docs are
fairly good but written from the implementor's perspective so they tend to
bury the lede. I didn't think it had the equvalent of Reagent's reactions
until I got towards the bottom of the react-redux page, which was the third or
fourth time I looked at the docs. I'm not sure if I just missed it or it got
added after I last looked.

~~~
mercurial
I started recently, but I find them fairly decent. And there is not much to
it, really.

> I didn't think it had the equvalent of Reagent's reactions until I got
> towards the bottom of the react-redux page

Is that the subscription mechanism? I have yet to find a use for it...

~~~
grayrest
> And there is not much to it, really.

The two use the same model but are organized differently so it throws me off a
bit.

Re-frame chooses to hide the actual reduction step so instead of the big
switch statement you have a bunch of registered event handlers, which are pure
functions take the state and event and return the new state. The middleware is
HoF around these so it's done per-handler as well as at the base reducer
level, which turns out to be really good for code reuse.

> Is that the subscription mechanism? I have yet to find a use for it...

I think they call it selections. Reactions let you transform a normalized data
model into a shape that's useful for the components. They also let you build
up chains of reusable calculations/conditions to shift complex conditionals
out of the components and into the model code so its co-located with the
handlers, which are split up by the part of the app they work with
(credentials, search, nav, etc).

~~~
mercurial
This sounds like an interesting approach, though I'm not really fan of events
(it's easy to see when they fire, but less easy to make sure anything is
listening).

------
Skywing
The Clojure, and ClojureScript, hype train is effective. I've been interested
in learning Clojure for several years. Every other week, or so, I'll see a
tweet or blog post that makes me want to drop JavaScript and move over to
ClojureScript. The community looks like a great one to be part of, from an
outsider's perspective. With that said, I've never been able to break beyond
that learning curve, or have that "ah hah!" moment. I've been able to learn a
lot of different languages over the years, and I so want Clojure to be one of
them but it just hasn't happened for me, yet. This blog post puts my issue
into words very well, it feels like I'm trying to program in mandarin and I
struggle to be efficient at what seem like the most basic of tasks. A script
that I could knock out in 20 minutes, with C# or JavaScript, will take me
hours or days in Clojure because of the lack of muscle memory or something. My
excitement always fades back into thoughts of "meh is it really worth the
effort", at which point I usually just revert back to JS. :(

------
pkese
RxJS has a steep learning curve and is quite hard to really understand beyond
basic examples that fit on a presentation slide. It might be a great tool for
really smart developers, but I wouldn't share any large RxJS code in a
developer team where all developers are not at least 200% geniuses.

The whole thing reminds me of C++ years ago, where the language allowed for
really smart and efficient coding style, but many less skilled developers at
the time had problems with deep understanding of templates, operator
overloading, multiple inheritance, various constructors and such. Pragmatic
companies (e.g. Mozilla) thus decided to ban all the advanced features and
stick just to basic ones so that the code would be maintainable.

I think RxJS is facing similar fate. I've spent quite some time learning it
and I think it is one of the more complex paradigms on the market today. In
addition there isn't much useful documentation. Feel free to check stack
overflow and find out about the struggles people are going through tying to
extend basic examples (e.g. google for rxjs mousedrag).

------
programminggeek
Building complex things is hard. Every tool breaks down when enough complexity
is thrown at it.

The answer is to build less complex things.

------
davexunit
I don't fully understand the argument against FRP here. It seems like the
author has passed on FRP in favor of coroutine-like asynchronous programming.
I don't view one as replacement for the other, but as different layers of an
asynchronous programming system. Coroutines are great for async and imperative
tasks, while FRP is great for async and functional tasks with persistent data.
My own FRP implementation [0] in Guile Scheme is built _on top of_ a coroutine
implementation [1], which in turn is built on top of Guile's first-class
delimited continuations. [2]

That said, the author specifically talks about RxJs and Bacon.js, both of
which have two major problems for me:

1) They don't satisfy the closure property. They both have two fundamental
data types: event streams and properties. Certain combinators expect streams,
and others expect properties. Compare this with API's like Elm's which just
have a single type: the signal. The closure property is satisfied here and
programming is much more pleasant.

2) They both deal with stream/property life cycles. Objects need to explicitly
unsubscribe from other objects, and streams may have a beginning and an end
(i.e. they may be marked as having no value or marked as being done producing
values). I think this is a mistake that complicates the API. FRP objects
should _always_ have a value, have no notion of being done, and not require
the equivalent of manual memory management to clean up. My Scheme
implementation uses weak references to automatically unsubscribe signals when
they are no longer referenced, which is basically only during development when
changing things at the REPL. Bacon and RxJS can't do something like this
because (and correct me if I'm wrong), no JS standard prior to ES6 has weak
data structures. Anyway, after all the hacking, the final program has a static
signal graph, just like Elm, which I think is the right way to do things.

[0]
[https://git.dthompson.us/sly.git/blob/HEAD:/sly/signal.scm](https://git.dthompson.us/sly.git/blob/HEAD:/sly/signal.scm)

[1]
[https://git.dthompson.us/sly.git/blob/HEAD:/sly/coroutine.sc...](https://git.dthompson.us/sly.git/blob/HEAD:/sly/coroutine.scm)

[2]
[https://www.gnu.org/software/guile/manual/html_node/Prompts....](https://www.gnu.org/software/guile/manual/html_node/Prompts.html)

~~~
WorldMaker
RxJS will automatically dispose/unsubscribe in composition scenarios: for
instance, if you flatMap from one "underlying" observable to a sequence of
observables, when you unsubscribe from the "underlying" observable it will
observe any remaining (if any) hot observables that were kicked off by the
flatMap.

RxJS also automatically disposes resources when an observable _completes_ and
maybe that's a part of what you are missing in the observable lifecycle and
part of why you've felt that RxJS has an equivalent of "manual memory
management"?

Yes, lots of streams are effectively infinite in nature, but that doesn't mean
that all of them are, and a completion signal can still be useful and
informative, including in lifecycle management. (It also helps keep the
duality between Enumerable/Iterator worlds and Observable/Observer worlds.)

One obvious case that I see a lot in applications where finite
streams/observables show up in great number is that Promise (Task/Future) is
essentially an observable that produces one result and completes.

~~~
davexunit
>RxJS will automatically dispose/unsubscribe in composition scenarios

But it still requires manually unsubscribing from something in the first
place. I just think that's the wrong approach to FRP. The graphs are actually
static, but dynamic behavior is needed for developing at the REPL.

~~~
WorldMaker
Thus far I've only had to manually dispose a single RxJS observable, and that
turned out to lead me to a better composition strategy and I got rid of the
manual dispose.

If I'm working in a REPL I set things up to "naturally complete" with some
useful sample set, just as if I were working with potentially infinite
enumerables. In RxJS that would typically be something like
myObservable.take(5).

------
bad_user
Core.async is not a substitute for Rx.

As a disclaimer I'm the author of an Rx-inspired library for Scala [1] and
that also works for Scala.js in the browser. Shameless plug aside, Scala also
has Future/Promise in its standard library and now due to macros support it
got scala/async [2], a library that gives you the "await" keyword in Scala, so
in Scala you also get this kind of M:N multithreading that looks like
synchronous code. This in addition to Akka and other possibilities.

In other words I've worked with both approaches and Core.async is not
comparable with Rx. I do understand the author's woes, as sometimes the Rx
model is misapplied, plus it's hard to understand for the unfamiliar.

One of my colleagues was complaining once that " _but I don 't know what the
debounce operator does and it's hard for me to read that_". And I told him:
_yeah dude, but try implementing the logic in debounce by yourself and see how
readable that is_.

And that's exactly why Rx is problematic. On one hand because stream
processing is fundamentally hard, no matter what model you choose. And on the
other hand Rx comes with a lot of useful operators that do a lot for you, but
then you have to learn about them.

For the naysayers, I'll just leave this piece of code with a challenge for
implementing it with core.async and compare in terms of readability and note
this is a copy paste from actual production code ...

    
    
        commands
          .groupBy(w => (w.assetID, w.commandID))
          .mergeMap { gr =>
            gr.timeout(30.seconds, Observable.empty)
              .throttleLast(1.second)
              .distinctUntilChanged
              .echoRepeated(5.seconds)
              .whileBusyBuffer(DropOld(30))
          }
    

What it does is to split the signals for each asset and command, for each of
these it's supposed to sample the signal by 1 second, but in case the same
value is repeated over and over again or in case the channel goes silent, then
the last value will end up signaled every 5 seconds (reducing the traffic to
our OpenTSDB). Finally for each key we close the stream after 30 seconds of
inactivity. And then for each such key it does buffering of at most 30
elements and in case the consumers are too slow, then these buffers start
dropping older elements on overflow. And then we merge everything back.

I would also show you how we are modeling state machines with the "scan"
operator, state machines that are evolved from signals coming from multiple
sources, but the sample would be too long. In any case "scan" allows you to
use pure functions and data-structures, so you can test your business logic
without interactions to third party services, mocks, stubs or whatever.

I have to deal with such code all the time. And I've seen such code
implemented in a classic fashion as well. You basically end up with Maps
storing stuff and with ifs and whiles and with manual timers in an unholy
dance of mutation so hard to understand and debug that it would make grown men
cry.

But then such solutions are not silver bullets. Rx, CSP, futures, actors are
not silver bullets to be applied everywhere, with all of them having a sweet
spot for which they excel. I'm actually using Rx, actors, futures, scala/sync
in the same project and it's great.

Also, one last note: Rx is not FRP ;-)

[1]
[https://github.com/alexandru/monifu/](https://github.com/alexandru/monifu/)

[2] [https://github.com/scala/async](https://github.com/scala/async)

~~~
tunesmith
As soon as OP started talking about Go and Clojure and Core.async, I wondered
if Scala.js would give him the same sort of benefits in this case as
ClojureScript. But I don't know a lot about Scala.js yet and if you can
actually run an actor system on the javascript frontend.

~~~
bad_user
You can run an actor system in Javascript of course and there are partial
ports of Akka available, but are not meant for production use. I wouldn't use
Akka in the browser though, as it's too heavy and I feel that the kind of
tasks meant for Akka do not happen in the browser, though for Node.js that
would be another story.

------
theknarf
If you feel that RxJS is a bit hard, but don't want to go all crazy and switch
to ClojureScript; then Redux is pretty good. It's not as "powerfull" as RxJS
and it has some guidelines on how you should structure code. It also have very
good tooling integrations, and debug-ability.

------
yrns
Once I found flyd [0] I never looked back at any of the other streams
libraries. It's very simple, easy to jump into, and doesn't have the
stream/property split. There's not a lot of code, so it doesn't make debugging
any harder.

Also, there is js-csp [1] if you want to use CSP without having to make the
jump to ClojureScript. Works great.

[0] [https://github.com/paldepind/flyd](https://github.com/paldepind/flyd) [1]
[https://github.com/ubolonton/js-csp](https://github.com/ubolonton/js-csp)

------
zackangelo
The article mentions that async/await functionality hasn't landed in
Javascript yet. I wonder if you can get the same functionality today using
scala.js [0] and the scala/async [1] library.

I believe it should work. The scala/async library is just a set of macros that
transform blocks of code using async/await into for comprehensions.

[0] [http://www.scala-js.org](http://www.scala-js.org) [1]
[https://github.com/scala/async](https://github.com/scala/async)

~~~
sotojuan
I have been using yortus' async/await library[1] for a simple personal project
and honestly it works fine. I don't know if it's "serious/production" ready
but it has a extensive README and examples.

[1]
[https://github.com/yortus/asyncawait](https://github.com/yortus/asyncawait)

------
Keats
What do you think of something like
[http://mweststrate.github.io/mobservable/](http://mweststrate.github.io/mobservable/)
?

~~~
dustingetz
That looks to me, a lot more complicated than it needs to be. I think that has
combined two separate things - atoms and cursors. Here they are in javascript:

[https://github.com/cjohansen/js-
atom/blob/master/atom.js](https://github.com/cjohansen/js-
atom/blob/master/atom.js)

[https://github.com/dustingetz/react-
cursor/blob/master/src/C...](https://github.com/dustingetz/react-
cursor/blob/master/src/Cursor.js)

Vastly less code than
[https://github.com/mweststrate/mobservable/tree/master/src](https://github.com/mweststrate/mobservable/tree/master/src).
Cursor+Atom has nothing to do with Rx or core.async, of course, and is
probably not the future. (I say this as the maintainer of react-cursor)

~~~
curveship
It looks like react-cursor is 1300 loc [1], mobservable is 1900 [2]. How is
that "vastly less code"?

1 - [https://github.com/dustingetz/react-
cursor/blob/master/dist/...](https://github.com/dustingetz/react-
cursor/blob/master/dist/react-cursor.js) 2 -
[https://github.com/mweststrate/mobservable/blob/master/dist/...](https://github.com/mweststrate/mobservable/blob/master/dist/mobservable.js)

~~~
dustingetz
The essential idea of a cursor can be implemented in about 10 lines, and
because I actually looked through the source code of mobservable, I understand
what they are essentially doing and how it requires a couple hundred lines.
Since we're talking ideas here and not implementations, measuring artifacts is
not helpful.

------
Matthias247
My personal experience was that there is not a one-thing-fits-all solution.
There are things that can be beautifully implemented with Rx, and there are
other things were I found that using Promises (probably in combination with
async/await) makes things clearer. My current project (which involes quite a
bit of Typescript code) uses everything from Rx, Promises up to plain old node
EventEmitters and streams, and I'm actually really happy with all of it.

------
thesorrow
RxJS is not designed to be pull-based which makes it hard to use it for
something else than UI code. I hope this will change with RxNext.

~~~
crucialfelix
I just built something using RxJS that is pull-based. The easiest way I found
was just to have a timer that calls subject.next(), and the subject calculates
its next value and calls this.onNext(value) which pushes to the subscribers.

I was expecting more pull based support.

Is this what you are talking about ?
[https://github.com/ReactiveX/RxJS/pull/138](https://github.com/ReactiveX/RxJS/pull/138)

It looks like its already merged in.

