Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Am I the only who finds RxJS to be really unintuitive?

I never had any issues in the past with futures/promises libraries, but for some reason I can never remember how to use RxJS.



My goto example for RxJS are event handlers. Heres an example that implements drag-and-drop (for which the vanillajs / d3js versions aren't nearly as pretty)

It basically translates to

1. Wait for a mousedown event

2. Start listening for mousemove events

2a. On each mousemove event, do something

2b. When a mouseup event occurs, stop listening for mousemoves and return to 1.

    import { fromEvent } from 'rxjs';
    import { exhaustMap, takeUntil, tap } from 'rxjs/operators';

    const el = document.querySelector('body');
    const mousedown$ = fromEvent<MouseEvent>(el, 'mousedown');
    const mousemove$ = fromEvent<MouseEvent>(document, 'mousemove');
    const mouseup$ = fromEvent<MouseEvent>(document, 'mouseup');

    mousedown$.pipe(
      exhaustMap(mousedownEvent => mousemove$.pipe(takeUntil(mouseup$))),
      tap(mousemoveEvent => {
        console.log('mousemove')
      })
    ).subscribe();
https://stackblitz.com/edit/drag-and-drop-rx-rvdkzv?file=ind...

---

More specifically I've found RxJS makes simple one-off calls (eg API fetches) ceremoniously painful (do I need to unsubscribe? do I get the value sync-ly or async?), but anything that involves multiple values (eg websockets, polling, event handlers) gets much cleaner.

My biggest complaint with RxJS is probably the dev experience though. It makes call stacks / the debugger useless so you have to get comfortable with console.log's everywhere. Which is one of the reasons I avoid chaining observables and filter()s in general.


Hmm I agree with call stacks, but the debugger is doing fine


RxJS, more specifically its primitive Observables, fills a different space from Promises. For simple cases where only one value is emitted Promise probably works better and is more intuitive, but there are certain things that Promises cannot model - specifically when there is a stream of events over time. RxJS is more complicated because it needs to model more complex things. I do think Angular made a big mistake overusing it in places where Promises would have worked perfectly fine, but when you do need it it does work pretty well.


> I do think Angular made a big mistake overusing it in places where Promises would have worked perfectly fine, but when you do need it it does work pretty well.

Was it really a mistake or were they just using it before native promises were widely supported?


Yes, RxJS is extremely hard to use. I developed on a lot of different technologies, but RxJS is one of the things I never seem to get a hang of.

It’s not just the syntax (so (many (brackets)) and => lambdas), but it’s also really hard to create a mental model for what’s happening.

I know that react hooks are not a replacement for Rx, but really often you can program similar functionality with hooks and it’s just 10 times easier to read.


Unintuitive? Yes.

If by unintuitive you mean “Can I look at it without training and know what’s going on.” But the problems they solve are also unintuitive. Only, those issues can be buried under a tangle of nested callbacks that look correct, intuitively.

Once you take the time to really (and I mean really) understand RxJS it’s incredible. Write your own Observables. Write your own operators. Learn what the built in operators are really doing (subscriptions, etc) reimplement them yourself.


I've seen components rewritten using observables to be sometimes a full 1/4 of the original size.

The biggest thing for me is the realization of the "source of truth". With observables, if they are set up correctly, you can see the exact bits of information that some resultant stream are dependent upon. Operations then can have a single action because all of those sources can be muxed together rather than having several calls throughout a component to doSomeAction().


First: This is not an attack on you

I have such an issue with this response because it just seems like “you’re holding it wrong” a la the iPhone “scandal” where apple attempted to blame an engineering flaw on its users

I would imagine most people’s use case (mine certainly is) for RxJS boils down to “call a JSON API and receive a response”. That shouldn’t be a hard problem

Imagine if someone complained about the complexity of Git and the answer was to “write your own DVCS”

The entire point of abstractions is that I don’t need to understand what’s going on underneath them


1) You skipped points one and two in my comment: a) Learn the foundations of RxJS. b) Understand how to extend RxJS by creating operators. Which lead to c) read RxJS’s code to understand how it implements operators to better learn to implement your own.

2) RxJS is not Git. It’s a library designed to be extended by users. To use RxJS you need to write code with it. Git is not exclusively a library and doesn’t require you to write you own extensions to use it day to day.

As a casual Git user you wouldn’t get much out of implementing a DVCS but you would benefit from learning to host your own repo instead of relying on GitHub.

If someone was struggling to use Git I’d tell them to learn the foundations, practice them, and then to host their own repos to continue learning.

The same basic track I recommended for RxJS.

A big moment for RxJS users is when they realize that they need an operator or observable that doesn’t exist. It’s really fun to write your own.


I think the most bad rep for RxJS comes from using it when it is not needed.

Parent comment said:

> But the problems they solve are also unintuitive.

Do you consider calling a JSON API unintuitive or complex? If not, then you may be using the wrong tool. If you need nothing else, you are perfectly fine using a promise.

If you need to await extra requests, transform them, and react to other events then you need RxJS. For a simple call, you do not.

> I would imagine most people’s use case (mine certainly is) for RxJS boils down to “call a JSON API and receive a response”. That shouldn’t be a hard problem

Do you consider the following code hard to understand or are you are making requests in a more complex way?

``` this.network.get('<url>').subscribe(response => <do whatever you want here>) ```

Even if we agree to disagree that the above code snippet is hard to understand, you can just convert it to a promise:

``` const response = await lastValueFrom(this.network.get('<url>')) ```


No, that call isn’t difficult. What is more difficult are examples given on things like Angular University, where there are pipe, subscribe, catchError, among others, in a single call chain. It’s not obvious to me at all what the order of execution is in this call chain for instance:

    http$
        .pipe(
            map(res =>     res['payload']),
            catchError(err =>     {
                console.log('caught mapping error and rethrowing', err);
                return throwError(err);
            }),
            finalize(() =>     console.log("first finalize() block executed")),
            catchError(err =>     {
                console.log('caught rethrown error, providing fallback value');
                return of([]);
            }),
             finalize(() => console.log("second finalize() block executed"))
    )
        .subscribe(
            res => console.log('HTTP response', res),
            err =>     console.log('HTTP Error', err),
            () =>     console.log('HTTP request completed.')
    );
Once you see the output it begins to finally make sense but intuitive it is not


If you look at a snippet of code in a language you don’t understand you wouldn’t call it intuitive. Once you learn the language you might see that what what was unintuitive before is intuitive and idiomatic now.

Once you learn how RxJS works examples like the one anbove anre intuitive.

Angular 2’s most egregious crime is that their tutorials try to make it (and RxJS) seem “simple”. They aren’t. They’re powerful.


Very much agree, and even for the more complicated use cases Saga's are much cleaner and I would argue easier to understand. Article below goes through a few of the differences.

https://shift.infinite.red/redux-observable-epics-vs-redux-s...


I worked for 4 years intensively with RXJS. I love the concept, but it quickly becomes a mess in readability when trying to merge different data sources.


Can you link/show an example?

I am doing that all the time and all it takes, is withLatestFrom or combineLatest


I feel like most of the RxJS functionality doesn't have a real use case. At the end of the day, you are doing an async call, something axios handles just fine. I did a bunch of presentations on RxJS, so I understand it fairly well, but I have never used it past basic API calls "irl".


> I feel like most of the RxJS functionality doesn't have a real use case.

I disagree. I have implemented several use cases using RxJS which would have otherwise been significantly more difficult to solve. (Think timing parallel sequences of animations und other UX effects etc.)


Rx can be awesome for some complex problems. But I’m angular it’s everywhere and often makes things much more complex than they have to be.


I’ve rarely seen it used beyond very, very basic things in Angular projects (and I’ve always wished I saw more of it, actually). Maybe we just work on totally different kinds of projects.


I am using ngrx, and you get using rxjs a lot in effects.


Seeing as it’s often. Do you have a single example?


I am the guy who doesn't use it, but people used to recommend using it for "event-driven architecture on the front end" - clicking a button is an event, typing text is an event, hovering over something is an event, and there could be multiple subscribers. The example use-cases people give are really contrived. The functional crowd also pushed it. Again, to me it's another trend like Web Components that never really took of in the mainstream. Yes, people use Web Components and RxJS' full power, but it's not something to learn when you are looking for practical, widely applicable skills.

If anyone still cares about this, here is a helpful site that you can understand without knowing anything about RxJS: https://rxmarbles.com/


The biggest benefit, which I haven't found much of a practical need for, is you can continue to emit 1...n times, whereas a Promise only resolves once. Since it's an indeterminate number of times, you need to subscribe and unsubscribe to prevent memory leaks, so it just adds more overhead. Use a Promise if you only need to resolve once.


Generators, yield, Iterator?


If you get on board with it fully in Angular, rxjs + async pipes can make things pretty clean.


I'm not sure if you have spent much time in a functional language but I found this helped me understand what is going on with a Promise and an Observable. They're all Monads. Just like List and Map or the optional Monad.

When I first started learning that sort of thing it made no sense at all. But now it's just a design pattern that allows you to organize things. Having that understanding helps abstract away some of the mechanisms in the observable.

It helped me at least. YMMV


Indeed! The concept of Monad is simple and very useful; once you learn it, you start seeing it everywhere.

JS Promises are not completely monadic though, because a Promise that resolves into a Promise is always effectively auto-flattened into a single Promise. But most of the time other monadic properties of Promises can be used despite this.


Good to know. I didn't realize that promises were not monads.


RxJS really needs a better debugging experience, I would love some visual representation (like a graph?) of all the events and how they relate to each other (who is waiting what, what has been waited, what was canceled by whom, etc). I tried a few plugins and browser extensions that supposedly do that but they all crashed, in their defence it was probably because it was a huge monolithic app.


Fixing something too complex by adding additional complexity? :)


A tool to help me get a mental model of it's doings would reduce complexity where it matters the most: inside the developer's brain, ha.


Sometimes you have to do more to do less…


I haven't used rxjs specifically a whole lot but I am a fan of the general declarative pipeline style that it promotes. A different JS library that I've used extensively that promotes a similar style but for more general purposes is Ramda. It is a very different approach to programming than is common so I understand not finding it easy to grasp. In my experience, the benefit is generally that there is less clutter in your code and the dataflow that is happening is a lot more explicit which helps me build a mental model of what is happening a lot more quickly.

ChatGPT is actually pretty good at exploring these different styles btw. It is pretty good at taking one code example and implementing it in different ways when prompted.


Same.

I've found that this is one of those things that half of the developers never learn to use properly and is therefore a net negative.

In Angular specifically it offers a spectrum of footguns, my favourite by far being passing an observable as an input.

Will the component re-render when the observable emits? Who knows? It depends on more than one factor.

Interesting things also happen when it's a cold observable, it errors out or just completes.

Overall it's quite the circus and I've been in teams where most of the developers had only a surface understanding of what was going on.

That in and of itself is actually not a showstopper until someone decides to create their own component library.


RxJS _used to be_ more intuitive, IMO. Around version 5 or so the API changed to use `.pipe` instead of simply chaining operators. Also, some functions were renamed to be (unintentionally, one assumes) more opaque. Such as `flatMapLatest` -> `switchMap`.

Anyhow, there's definitely a learning curve but what it provides is very powerful. It's unfortunate, IMO, that the API has become less approachable over the years.


Although I see how chaining might feel more intuitive, pipeable operators allow for a lot more intuitive and readable composition, making it so you can create reusable and easily tested operators, or even sets of them.

It’s great for what it’s designed for, but you’re not wrong either. Composition can be hard to manage and navigate where a chainable API can be easy to follow and interpret by simply reading the flow.

I think RxJS is actually amazing and I like the path they chose, but I see why it isn’t more widely adopted too. It isn’t nearly as intuitive as it maybe could be.


There's definitely much to be said in favor of simple function composition.

However, I found the API choices odd because in RxJS the `.pipe` operator is, itself, chained rather than being standalone. Using a standalone `pipe`, such as via Ramda or something home-grown worked just fine, so I found it very strange that they mixed the functional composition style with the object-chainable style.

> I see why it isn’t more widely adopted too

I agree that RxJS is amazing, which is why less adoption is unfortunate. So many projects would benefit from RxJS or something like it.


What's concerning is that when people use Observables, 90% of the time you need to break out of them again.

Subscribe, (value) => currentValue = value, do some calculation, onDestroy unsubscribe..

I don't know whether people really solve complex problems or whether they just appear complex when you use RxJS.


Have you tried `async` pipe and `takeUntilDestroyed`? I only ever `subscribe` if I need to trigger side effects, like setting a form validator based or a value (probably can be replaced by async validators) or modify DOM outside of the app root.


Yes, but the problem with the async pipe is that you're pulling logic into the template.


The problem with RxJS is that it feels like Node did in the beginning. It's hard as eff to figure out where the system goes, what happens when, in what order etc.


Yes, and that's part of why they're adding signals.


you still can use promises if you want?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: