You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I wonder whether there has been a discussion about this topic already (presumably yes, but I couldn't find it, so please forgive me in case I've reopened this one ...)
The problem
I'm working on a rather large code base (Angular SPA with more than 100.000k lines of business code). We're having the same issues again and again, which is the "randomness" of Observables in terms of whether they complete or not.
On the one hand, it is desirable to provide "durable observables" (i.e. let me define them as "potentially emitting multiple times without guaranteed completion") to be able to work with a Redux Store (in our case NgRx Store) and have long running subscriptions that flush new results automatically to wherever they're needed. On the other hand, this regularly leads to (sometimes rather hard to) detect bugs, for example:
it is very easy to make mistakes by not cleaning up subscriptions properly, which almost always leads to severe memory leaks (and those really are HARD to debug!)
some operators silently do nothing in case their upstream input observables don't complete, e.g. forkJoin() (good luck debugging things where just "nothing happens at all").
Angular Resolvers and Guards only go on when the given observable completes
Mitigations
There are some ways to mitigate these problems, e.g. like:
using ESlint Rules that require a pipe(takeUntil(someDestroyTrigger$)) to come just before calling .subscribe() but it feels like an ill-fitted workaround because the rule also applies when you know, that your observable completes.
always assuming every observable is durable, thus throwing around take(1) operators all over the place which feels like littering to me. Plus: untrained programmers can easily forget to do so
What could RxJS do about it?
A distinction could be made between "one-time observables" (i.e. let me define them as "automatically completing after the first emission") and "durable observables" (definition see above). It could be baked into the type system, flushing types down all (upstream) pipes right to the consumer of an observable so it is known exactly what to expect. These (new) types could help to avoid having to add take(1) (or takeUntil(someDestroyTrigger$) in places where "guaranteed completion" is a necessity. The ESLint rule I've mentioned above, for example, could stay silent in case it sees we're subscribing to a "one-time observable".
Summary
While I personally like RxJS very much because it allows to do things in a top-down-data-flow approach, I think it's too error prone for untrained programmers (or let's say "full stack" developers that do 80% backend and 20% frontend). I would not recommend to use it in a new project given its current state. It is far too easy to introduce severe memory leaks into the application.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
Hi there
I wonder whether there has been a discussion about this topic already (presumably yes, but I couldn't find it, so please forgive me in case I've reopened this one ...)
The problem
I'm working on a rather large code base (Angular SPA with more than 100.000k lines of business code). We're having the same issues again and again, which is the "randomness" of Observables in terms of whether they complete or not.
On the one hand, it is desirable to provide "durable observables" (i.e. let me define them as "potentially emitting multiple times without guaranteed completion") to be able to work with a Redux Store (in our case NgRx Store) and have long running subscriptions that flush new results automatically to wherever they're needed. On the other hand, this regularly leads to (sometimes rather hard to) detect bugs, for example:
forkJoin()
(good luck debugging things where just "nothing happens at all").Mitigations
There are some ways to mitigate these problems, e.g. like:
pipe(takeUntil(someDestroyTrigger$))
to come just before calling.subscribe()
but it feels like an ill-fitted workaround because the rule also applies when you know, that your observable completes.take(1)
operators all over the place which feels like littering to me. Plus: untrained programmers can easily forget to do soWhat could RxJS do about it?
A distinction could be made between "one-time observables" (i.e. let me define them as "automatically completing after the first emission") and "durable observables" (definition see above). It could be baked into the type system, flushing types down all (upstream) pipes right to the consumer of an observable so it is known exactly what to expect. These (new) types could help to avoid having to add
take(1)
(ortakeUntil(someDestroyTrigger$
) in places where "guaranteed completion" is a necessity. The ESLint rule I've mentioned above, for example, could stay silent in case it sees we're subscribing to a "one-time observable".Summary
While I personally like RxJS very much because it allows to do things in a top-down-data-flow approach, I think it's too error prone for untrained programmers (or let's say "full stack" developers that do 80% backend and 20% frontend). I would not recommend to use it in a new project given its current state. It is far too easy to introduce severe memory leaks into the application.
What do you think?
Beta Was this translation helpful? Give feedback.
All reactions