DEV Community

Cover image for RxJS - I Have A Bone To Pick With You
Dean Radcliffe
Dean Radcliffe

Posted on • Edited on

RxJS - I Have A Bone To Pick With You

RxJS, there are some things I really love about you, but I think I'm falling for someone new - π—₯π˜…π‘“π‘₯

Your popularityβ€” You have more downloads a month than React and Angular combined.

Your maturityβ€” You're a ripe 13 years old. Forever in technology time, and 3 years older than Promises, even.

Your utilityβ€” For eliminating race conditions, all forms of timing control, and resource managementβ€” you have no equal.

But RxJSβ€” with all this time you've been around, how come everybody doesn't know that you're the best already? How did you let React become the biggest framework, with useEffect and its quirks, and not even have them build upon you - the best choice available at the time? Now there are all these React developers out there who don't know the joy and power of working with you, RxJS.

But I know you're thinking what React did can't be your fault right? I mean - your documentation is perfect right? No friction to onboarding at-all, right...

So to help you out, I'm going to tell you about your DevRel issues (DevRel) and how to fix them. To start, you gotta do something about your...

Confusing Concurrency Docs

Your documentation for your *Map operators is tragically hard to read. In the v6 docs you say exhaustMap:

Projects each source value to an Observable which is merged in the output Observable only if the previous projected Observable has completed.

So, a previously running Observable Subscription blocks others from starting, you mean.

You could have called it something resembling Blocking.

Your concurrency operators (mergeMap, concatMap, switchMap, and exhaustMap) focus on the notifications themselves, instead of the concurrency strategy that produces them.

This mapping operator could be illustrated by a picture:

Blocking Concurrency Mode

Or the family of all of them illustrated as cards with common names and use cases.

RxFx cards

That will straighten out your comprehensability issue around these operators.

Observable vs Subscription Confusion

Developers who understand Promises often fail to realize that an Observable is doing nothing without a Subscription. This is what makes an Observable not something you can await.

But RxJS, you sometimes fail to differentiate sufficiently between an Observable and a Subscription in the docs! When the exhaustMap docs said: "only if the previous projected Observable has completed" - that should have said "the previously running Subscription has completed". Observables are templates for work. Only subscriptions can deliver values.

I don't think calling an Observable multicast helps either. An Observable, being like a command, can be executed several times - we dont say that the command is multicasting to its executions.

I don't like calling an Observable a 'stream' of notifications either - it may not have any! I prefer to say that an Observable is a template for creating a process - containing a start function and a cleanup function.

So - terminology is confusing. But it's also unclear where to split up a big chain of Observables.

App Partitioning / Where to Call Subscribe

Some schools of RxJS believe in constructing a giant chain of Observables, with some higher-order ones in the middle, and calling .subscribe() once at the end of it only. The Swipe-To-Refresh example from LearnRxJS.io does this.

swipe to refresh demo

Check out its code on StackBlitz. I don't know what you thought separation of concerns was supposed to be - but I don't think that is it.

What are good practices, when combining Observables, especially higher-order ones? If operators are the individual words of your code paragraphs, what does a sentence-level structure look like? If we had that it might help us solve...

Operator Soup

The Swipe-To-Refresh example from LearnRxJS.io starts out with 19 imported operators and functions with raw RxJS. With π—₯π˜…π‘“π‘₯ you implement the same functionality with 6. That's 66% less you have to fit into your mind at once, 13 fewer operators to understand.

Some of this comes from demo sites wanting to show all operators in use. But it's not uncommon to find a ton of operators in real RxJS production code. While each one is purposeful, their many combinations can be confusing.

If we could use fewer operators, we would have less code, and not suffer from as many...

Errors

This is already a well covered topic, RxJS. But you already know it's a source of pain and confusion. Recent changes have helped producers interfere with each other less, but the risk of UnhandledRejectionError bringing down a process is still large.

Why can't we specify 'containers' that can individually terminate on error, without risk to the overall process?

You see, we need some help, RxjS.

Solution - π—₯π˜…π‘“π‘₯

When a community and library are mature enough, some "contrib" library emerges that the community puts its best value-add goodies in. That is what π—₯π˜…π‘“π‘₯ aims to be.

π—₯π˜…π‘“π‘₯ is RxJS "The Good Parts". With infinite expansion possibilities via 'raw' RxJS.

Problems like Confusing Docs, Observable vs Subscription Confusion, App partitioning, and Operator Soup, and even Errors are reduced when π—₯π˜…π‘“π‘₯ is your interface to RxJS.

Think of π—₯π˜…π‘“π‘₯ as sugar around an RxJS Subject that acts as an event bus. Each subscriber we call a "listener", and it is a concurrency-controlled, independent Subscription that is error-isolated from all others.

Basically, this means you can get more done with less skipping the several years you need to be fluent in RxJS.

Case Studyβ€” RxJS Swipe-To-Refresh Demo

The Swipe-To-Refresh Demo starts out with 19 imported operators and functions with raw RxJS. In π—₯π˜…π‘“π‘₯ - it needs only 6.

I'll dissect and rebuild it in my next post :)

Links


Top comments (0)