4 ms·
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
by vikingcaffiene 8y ago
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.
- steve_taylor 8y agoAt my job, we’ve been using observables in our React front-ends for at least the last three years. These are high traffic consumer websites. We’ve had great success with them and haven’t experienced any of the issues you describe. I guess the key is to minimise the amount of code that directly subscribes, don’t mutate data as it flows through an observable, and where possible, prefer deriving one observable from another over using imperative code to push data from one observable into another.
- jchw 8y agoIn React you can use higher order components and whatnot to handle subscriptions, and that capability most unfortunately is not available on all frameworks. Perhaps most egregiously, Angular doesn't really have any concept that works in place of HoCs, though at least you can sometimes fold observables using the async pipe.
- Udik 8y agoCan 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 8y agoI'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
- JTenerife 8y agoThis does a good job: https://gist.github.com/staltz/868e7e9bc2a7b8c1f754 https://gist.github.com/staltz/868e7e9bc2a7b8c1f754 We are using reactive programming basically everywhere. Example (Angular): The Angular Router has an Observable "paramMap". We don't subscribe to it but are creating another Observable for the data that should be loaded. getData$ = ( paramMap$: Observable<ParamMap> ): Observable<MyData> => { return paramMap$.pipe( // Create a query string map(paramMap => { // some pure function - business logic return getSomeQueryBasedOnTheRouteParams( paramMap ); }), // get the data // select$ is a method on the customStore that wraps the http client and does some more stuff // The switchMap is actually one of the concepts that I found initially not the easiest to understand switchMap(someQuery=> { return this.customStore.select$({ query: someQuery }); }), map(rawData => { // pure function with business logic return modifyData(rawData); }), shareReplay() ); }; Then it goes on with filtering, etc. getFilteredData$ = ( // filter:$ is in our case a simple Subject we're calling next(newFilterValue) // NGRX, NGXS or Akita are popular state management libraries to manage this kind of state filter$: BehaviourSubject<string>, data$: Observable<MyData>): Observable<MyData> => { combineLatest(filter$, data$).pipe( // Do the filtering using pure functions map( ... return myFilterFuntion(filter, data); ) ) }; The filteredData$ Observable can be consumed via async pipe in a component. The business logic is the same as in imperative-pull code. But using the abstraction of RxJS a lot of boilerplate and indirection vanishes. RxJS is not the easiest abstraction to learn (for me), but as with all, once you're comfortable a lot of the likely intimidating code above will become familiar. You just focus on writing business-logic in pure functions and combine it with RxJS.
- jondubois 8y agoAbout reactive WebSockets, I wrote a client and server library which lets you consume WebSocket data streams in a reactive way using pure async/await syntax (no callbacks, no Observables): https://asyngular.io https://asyngular.io It encourages the use of IIFEs as a way to declaratively isolate parallel async operations from serial async operations. Also it shifts control to the data consumer instead of the producer; this should reduce the likelihood of memory leaks.
- JTenerife 8y ago>> because of the side effects that get caused from subscriptions. Hmmmm, I wonder how a subscription can cause side effects? I can only think of mutating the data in the subscription. But that should not be done. We are using RxJS with great success but you should go all the way with Observables: All computations/combination/etc. should be done in pipe()s. If you need to combine some plain data with Observables create a subject for the plain data and pipe(combineLatest(),map(), ...). Don't mix it into subscriptions. Subscribe at the very end of the chain. No modification in subscribtions.
- willtim 8y ago> Hmmmm, I wonder how a subscription can cause side effects? A subscription is a side effect.
- duiker101 8y agoReading your post for a moment I thought we worked at the same place. I had the exact same experience (but with RxJava)