Reconnecting a WebSocket with a shared RxJS observable - javascript

I have an observable like this:
const records$ =
Rx.DOM.fromWebSocket('ws://192.168.2.4:9001/feed/', null)
.map(ev => parseRecord(ev.data))
.share();
I have many subscribers. When the connection is lost, all subscribers unsubscribe:
let records$Subscription;
records$Subscription = records$.subscribe(
record => { ... },
error => records$Subscription.dispose()
);
I verified that the call to dispose is indeed being made once for every subscription. Therefore, the share refcount has reached zero.
However, when I now subscribe to records$ again, no new WebSocket connection is set up. When I remove the call to share, however, it is. How come this doesn't work as expected with share?

I believe in rxjs v5, share does allow you to reconnect but not in Rxjs v4.
In Rxjs 4, share is bascially multicast.refCount and once the subject used for the multicast is completed, it cannot be reused (per Rxjs grammar rules, have a look at What are the semantics of different RxJS subjects? too), leading to the behaviour you observed.
In Rxjs 5, it uses a subject factory (something like multicast(() => new Rx.Suject().refCount())), so a subject is recreated when necessary.
See issues here and here for more details.
In short, if you can't do with the current behavior, you can switch to the v5 (note that it is still in beta and there are some breaking changes).

Related

When should I create a new Subscription for a specific side effect?

Last week I answered an RxJS question where I got into a discussion with another community member about: "Should I create a subscription for every specific side effect or should I try to minimize subscriptions in general?" I want to know what methology to use in terms of a full reactive application approach or when to switch from one to another. This will help me and maybe others to avoid unecesarry discussions.
Setup info
All examples are in TypeScript
For better focus on question no usage of lifecycles/constructors for subscriptions and to keep in framework unrelated
Imagine: Subscriptions are added in constructor/lifecycle init
Imagine: Unsubscribe is done in lifecycle destroy
What is a side effect (Angular sample)
Update/Input in the UI (e.g. value$ | async)
Output/Upstream of a component (e.g. #Output event = event$)
Interacton between different services on different hierarchies
Exemplary usecase:
Two functions: foo: () => void; bar: (arg: any) => void
Two source observables: http$: Observable<any>; click$: Observable<void>
foo is called after http$ has emitted and needs no value
bar is called after click$ emits, but needs the current value of http$
Case: Create a subscription for every specific side effect
const foo$ = http$.pipe(
mapTo(void 0)
);
const bar$ = http$.pipe(
switchMap(httpValue => click$.pipe(
mapTo(httpValue)
)
);
foo$.subscribe(foo);
bar$.subscribe(bar);
Case: Minimize subscriptions in general
http$.pipe(
tap(() => foo()),
switchMap(httpValue => click$.pipe(
mapTo(httpValue )
)
).subscribe(bar);
My own opinion in short
I can understand the fact that subscriptions make Rx landscapes more complex at first, because you have to think about how subscribers should affect the pipe or not for instance (share your observable or not). But the more you separate your code (the more you focus: what happens when) the easier it is to maintain (test, debug, update) your code in the future. With that in mind I always create a single observable source and a single subscription for any side effect in my code. If two or more side effects I have are triggered by the exact same source observable, then I share my observable and subscribe for each side effect individually, because it can have different lifecycles.
RxJS is a valuable resource for managing asynchronous operations and should be used to simplify your code (including reducing the number of subscriptions) where possible. Equally, an observable should not automatically be followed by a subscription to that observable, if RxJS provides a solution which can reduce the overall number of subscriptions in your application.
However, there are situations where it may be beneficial to create a subscription that is not strictly 'necessary':
An example exception - reuse of observables in a single template
Looking at your first example:
// Component:
this.value$ = this.store$.pipe(select(selectValue));
// Template:
<div>{{value$ | async}}</div>
If value$ is only used once in a template, I'd take advantage of the async pipe and its benefits for code economy and automatic unsubscription. However as per this answer, multiple references to the same async variable in a template should be avoided, e.g:
// It works, but don't do this...
<ul *ngIf="value$ | async">
<li *ngFor="let val of value$ | async">{{val}}</li>
</ul>
In this situation, I would instead create a separate subscription and use this to update a non-async variable in my component:
// Component
valueSub: Subscription;
value: number[];
ngOnInit() {
this.valueSub = this.store$.pipe(select(selectValue)).subscribe(response => this.value = response);
}
ngOnDestroy() {
this.valueSub.unsubscribe();
}
// Template
<ul *ngIf="value">
<li *ngFor="let val of value">{{val}}</li>
</ul>
Technically, it's possible to achieve the same result without valueSub, but the requirements of the application mean this is the right choice.
Considering the role and lifespan of an observable before deciding whether to subscribe
If two or more observables are only of use when taken together, the appropriate RxJS operators should be used to combine them into a single subscription.
Similarly, if first() is being used to filter out all but the first emission of an observable, I think there is greater reason to be economical with your code and avoid 'extra' subscriptions, than for an observable that has an ongoing role in the session.
Where any of the individual observables are useful independently of the others, the flexibility and clarity of separate subscription(s) may be worth considering. But as per my initial statement, a subscription should not be automatically created for every observable, unless there is a clear reason to do so.
Regarding Unsubscriptions:
A point against additional subscriptions is that more unsubscriptions are required. As you've said, we would like assume that all necessary unsubscriptions are applied onDestroy, but real life doesn't always go that smoothly! Again, RxJS provides useful tools (e.g. first()) to streamline this process, which simplifies code and reduces the potential for memory leaks. This article provides relevant further information and examples, which may be of value.
Personal preference / verbosity vs. terseness:
Do take your own preferences into account. I don't want to stray towards a general discussion about code verbosity, but the aim should be to find the right balance between too much 'noise' and making your code overly cryptic. This might be worth a look.
if optimizing subscriptions is your endgame, why not go to the logical extreme and just follow this general pattern:
const obs1$ = src1$.pipe(tap(effect1))
const obs2$ = src2$pipe(tap(effect2))
merge(obs1$, obs2$).subscribe()
Exclusively executing side effects in tap and activating with merge means you only ever have one subscription.
One reason to not do this is that you’re neutering much of what makes RxJS useful. Which is the ability to compose observable streams, and subscribe / unsubscribe from streams as required.
I would argue that your observables should be logically composed, and not polluted or confused in the name of reducing subscriptions. Should the foo effect logically be coupled with the bar effect? Does one necessitate the other? Will I ever possibly not to want trigger foo when http$ emits? Am I creating unnecessary coupling between unrelated functions? These are all reasons to avoid putting them in one stream.
This is all not even considering error handling which is easier to manage with multiple subscriptions IMO

In RxJS, why does a pipe get executed once for each subscription?

I want to have multiple subscriptions to react to an observable event, but I want to log the event as well, so I pipe it through a do() operator in which I do the logging.
The problem is, the event gets logged once for each of the subscriptions I create!
I'm getting around this at the moment by creating a Subject and calling next on it from an event callback, which allows me to log the event once and trigger multiple subscriptions as well.
Here is some code that demonstrates the issue: https://stackblitz.com/edit/rxjs-xerurd
I have a feeling I'm missing something, isn't there a more "RxJS" way of doing this?
EDIT:
I'm not asking for a difference between hot & cold observable, in fact I was using a hot observable - the one created by fromEvent() and was wondering why does my presumably hot event source behave like it's cold.
I realize now - after reading about share() - that pipe() "turns" your observable cold i.e. returns a cold one based on the your source (which may be cold, may be hot)
Because Observable sequences are cold by default, each subscription will have a separate set of site effects.
If you want your side effect to be executed only once - you can share subscription by broadcasting a single subscription to multiple subscribers. To do this you can use share, shareReplay, etc.
To better understand how it works, what is "cold" and publish, refer to the RxJS v4 documentation:
4.8 Use the publish operator to share side-effects
EDIT : share() is finally working. Please have a look to the comments below. Thx to #Oles Savluk.
I let my answer below for the records. It may help.
share() and multicasting stuffs did not solve my very similar issue.
Here is how I solved it : https://stackblitz.com/edit/rxjs-dhzisp
const finalSource = new Subject();
fromEvent(button3, "click").pipe(
tap(() => {
console.log("this happens only once")
})
).subscribe(() => finalSource.next())
finalSource.subscribe(
() => console.log("first final subscription")
)
finalSource.subscribe(
() => console.log("second final subscription")
)
finalSource.subscribe(
() => console.log("third final subscription")
)

What is the difference between Observable and a Subject in rxjs?

I was going through this blog and reading about Observables and couldn't figure out the difference between the Observable and a Subject.
In stream programming there are two main interfaces: Observable and Observer.
Observable is for the consumer, it can be transformed and subscribed:
observable.map(x => ...).filter(x => ...).subscribe(x => ...)
Observer is the interface which is used to feed an observable source:
observer.next(newItem)
We can create new Observable with an Observer:
var observable = Observable.create(observer => {
observer.next('first');
observer.next('second');
...
});
observable.map(x => ...).filter(x => ...).subscribe(x => ...)
Or, we can use a Subject which implements both the Observable and the Observer interfaces:
var source = new Subject();
source.map(x => ...).filter(x => ...).subscribe(x => ...)
source.next('first')
source.next('second')
Observables are unicast by design and Subjects are multicast by design.
If you look at the below example, each subscription receives the different values as observables developed as unicast by design.
import {Observable} from 'rxjs';
let obs = Observable.create(observer=>{
observer.next(Math.random());
})
obs.subscribe(res=>{
console.log('subscription a :', res); //subscription a :0.2859800202682865
});
obs.subscribe(res=>{
console.log('subscription b :', res); //subscription b :0.694302021731573
});
This could be weird if you are expecting the same values on both the subscription.
We can overcome this issue using Subjects. Subjects is similar to event-emitter and it does not invoke for each subscription. Consider the below example.
import {Subject} from 'rxjs';
let obs = new Subject();
obs.subscribe(res=>{
console.log('subscription a :', res); // subscription a : 0.91767565496093
});
obs.subscribe(res=>{
console.log('subscription b :', res);// subscription b : 0.91767565496093
});
obs.next(Math.random());
Both of the subscriptions got the same output value!
Observables
They are cold: Code gets executed when they have at least a single observer.
Creates copy of data: Observable creates copy of data for each observer.
Uni-directional: Observer can not assign value to observable(origin/master).
The code will run for each observer . If its a HTTP call, it gets called for each observer.
if its a service we want to share among all the components, it wont have latest result all new subscribers will still subscribe to same observable and get value from scratch
Unicast means can emit values from the observable not from any other component.
Subject
They are hot: code gets executed and value gets broadcast even if there is no observer.
Shares data: Same data get shared between all observers.
bi-directional: Observer can assign value to observable(origin/master).
If are using using subject then you miss all the values that are broadcast before creation of observer. So here comes Replay Subject
multicast, can cast values to multiple subscribers and can act as both subscribers and emmitter
I found the accepted answer slightly confusing!
An Observer isn't the interface for feeding an Observable source, it's the interface for observing an Observable source... which makes more sense from the name, right?
So, the reason that:
var observable = Observable.create(observer => {
observer.next('first');
observer.next('second');
...
});
works - creating an observable which emits 'first' then 'second' - is that the argument to Observable.create(...) is a subscribe function, it basically defines which Observer events will happen on a direct Observer of that Observable.
If you want to go into it a little bit further again, it's important to understand that the subscribe function isn't directly called on the Observer object when you subscribe, instead it's mediated by a Subscription object which can enforce correct observable rules, e.g. that an Observable will never emit a new value after observer.complete() has been called, even if your subscribe function looks as if it would.
REF: http://reactivex.io/rxjs/manual/overview.html#creating-observables
A Subject is both an Observable and an Observer and once again it looks just like the Observer interface is the way to 'feed' events to the Subject. But it's easier to understand the naming if you realise that a Subject is a bit like an Observable with the equivalent of the subscribe function (i.e. where you define what events will happen to things observing it) sitting there right on the object, even after it has been created. So, you call Observer methods on the Subject to define what Observer events will happen on things observing it! 😊 (And again, there are intermediate objects involved, to make sure that you can only do legal sequences of things.)
REF: http://reactivex.io/rxjs/manual/overview.html#subject
See rxjs document (more information and examples there):
http://reactivex.io/rxjs/manual/overview.html#subject
What is a Subject? An RxJS Subject is a special type of Observable that allows values to be multicasted to many Observers. While plain Observables are unicast (each subscribed Observer owns an independent execution of the Observable), Subjects are multicast.
A Subject is like an Observable, but can multicast to many Observers. Subjects are like EventEmitters: they maintain a registry of many listeners.
and code, Subject extending Observable: https://github.com/ReactiveX/rxjs/blob/master/src/internal/Subject.ts#L22
/**
* #class Subject<T>
*/
export class Subject<T> extends Observable<T> implements SubscriptionLike {
//...
}
Imagine if you have a stream of data coming into your application like in a websocket connection. You want a way to handle it. There is a few solution:
1. normal ajax request:
This solution is not viable because it is
not applicable to process push data. It is more of a pull then a
push.
2. Promise:
Also not good because you have to trigger them and
they can only retrieve once. Also more of a pull then a push.
So in order to retrieve this data, in the old time, we do a long-polling. Which is where we set an interval function to retrieve that stream of data every 1 minute for an example. Though it works, it actually burdening resources like CPU and memory.
But now with option no 3,
3. Observable: You can subscribe and let the stream of data to come
in non-stop until the function complete has been called.
Cool right ? But then there is another problem. What if you want to observe incoming data only once somewhere in your application. But you want to use that data simultaneously around your application when the data arrived. That is when and where you use Subject.
You place subject.subscribe() at places you want to use throughout your application. When the data arrived, places where there is subject.subscribe() will process them simultaneously. But the observer must subscribe with the subject as its argument like this.
observer.subscribe(subject).
Example application is when you want to build a notification alert.
You cannot have multiple subscription of the same observable because chances are, each subscribers will received different input data. But with subject, all that subscribe() through subject will be retrieving the same data.
Another analogy is through magazine subscription. Each subscribers will received the magazine with their name on it. So, different subscription = different receiver name.(Normal Observable)
But when you share with your friends, all of your friend would receive the same magazine with only your name on it.(Normal Observable with Subject)
This guy explain it very well with code example. You can check it out at https://javascript.tutorialhorizon.com/2017/03/23/rxjs-subject-vs-observable/
Hopefully this answer helps.
Briefly,
subject: you can send to it and receive from it.
Observable: you can receive from it only.
In another words,
In subject you can subscribe to it and you can use it to broadcast to other subscribers any time and anywhere in code.
whilst,
in observable you can subscribe to it only (you can't use it to broadcast data after it have been initialized).
The only place you can broadcast data from observable is inside its constructor.
Observable can inform only one observer, while Subject can inform multiple observers.
From another perspective, it is good to note that the subscription to an Observable re-execute the Observable function. This can lead performance issue if the data source is a service for instance.
If you want several subscribers to get the same value, you may need a Subject.
For this, make sure that your subscription is set before the Subject subscribed to the data source. Otherwise, your process would be stuck.
More details here: https://javascript.tutorialhorizon.com/2017/03/23/rxjs-subject-vs-observable/
Observable:
Only the Observable knows how and when the events are triggered on the observable. i.e the next() method has to be called only inside the instantiated constructor. Also, on subscribing each time, a separate observer is created and calls next() method using particular observer inside constructor only, in the following example subscriber itself is the observer and it is subscribed when the instantiated constructor gets executed.
Ex:
import { Observable } from 'rxjs';
const observable = new Observable(subscriber => {
subscriber.next(1);
subscriber.next(2);
setTimeout(() => {
subscriber.next(3);
}, 1000);
});
Subject:
Here next() method can be used by subject anywhere outside the constructor. Also, when next() method is called before subscribing, the particular event will be missed. Hence next() method has to be called only after subscribing.
Ex:
import { Subject } from 'rxjs';
const subject = new Subject<number>();
subject.next(1); // this is missed
subject.subscribe({
next: (v) => console.log(`observerA: ${v}`)
});
subject.subscribe({
next: (v) => console.log(`observerB: ${v}`)
});
subject.next(2);

How to cleanly reconnect to a ReplaySubject while avoiding past memoization side-effects?

I hold state in one ReplaySubject that replays the last copy of the state. From that state, other ReplaySubjects are derived to hold...well, derived state. Each replay subject need only hold it's last calculated state/derived state. (We don't use BehaviorSubjects because they always give a value, but we only want a value derived from our parent observables.) It is always necessary to replay the value to new subscribers if we have already generated derived state.
I have a custom observable operator that accomplishes this in just the way I want it to, but it doesn't feel that clean. I feel like there should be an efficient way to accomplish this with RxJ's operators themselves.
I have tried the two most obvious approaches, but there are slight problems with each. The problem involves unsubscribing and re-subscribing.
Open the fiddle below, open your console, and click run. I will describe the problem with each output.
https://jsfiddle.net/gfe1nryp/1/
The problem with a refCounted ReplaySubject
=== RefCounted Observable ===
Work
Subscription 1: 1
Work
Subscription 1: 2
Work
Subscription 1: 3
Unsubscribe
Resubscribe
Subscription 2: 3
Work
Subscription 2: 6
Work
Subscription 2: 7
Work
Subscription 2: 8
This works well, the intermediate functions don't do any work when there is nothing subscribed. However, once we resubscribe. We can see that Subscription 2 replays the last state before unsubscribe, and then plays the derived state based on the current value in the base$ state. This is not ideal.
The problem with connected ReplaySubject
=== Hot Observable ===
Work
Subscription 1: 1
Work
Subscription 1: 2
Work
Subscription 1: 3
Unsubscribe
Work
Work
Work
Resubscribe
Subscription 2: 6
Work
Subscription 2: 7
Work
Subscription 2: 8
This one does not have the same problem as the refCounted observable, there is no unnecessary replay of the last state before the unsubscription. However, since the observable is now hot, the tradeoff is that we always do work whenever a new value comes in the base$ state, even though the value is not used by any subscriptions.
Finally, we have the custom operator:
=== Custom Observable ===
Work
Subscription 1: 1
Work
Subscription 1: 2
Work
Subscription 1: 3
Unsubscribe
Resubscribe
Work
Subscription 2: 6
Work
Subscription 2: 7
Work
Subscription 2: 8
Ahh, the best of both worlds. Not only does it not unnecessarily replay the last value before unsubscription, but it also does not unnecessarily do any work when there is no subscription.
This is accomplished by manually creating a combination of RefCount and ReplaySubject. We keep track of each subscriber, and when it hits 0, we flush the replay value. The code for it is here (and in the fiddle, of course):
Rx.Observable.prototype.selectiveReplay = function() {
let subscribers = [];
let innerSubscription;
let storage = null;
return Rx.Observable.create(observer => {
if (subscribers.length > 0) {
observer.next(storage);
}
subscribers.push(observer);
if (!innerSubscription) {
innerSubscription = this.subscribe(val => {
storage = val;
subscribers.forEach(subscriber => subscriber.next(val))
});
}
return () => {
subscribers = subscribers.filter(subscriber => subscriber !== observer);
if (subscribers.length === 0) {
storage = null;
innerSubscription.unsubscribe();
innerSubscription = null;
}
};
});
};
So, this custom observable already works. But, can this be done with only RxJS operators? Keep in mind, potentially there could be more than a couple of these subjects linked together like this. In the example, I'm only using one linking to the base$ to illustrate the issue with both vanilla approaches I've tried at the most basic level.
Basically, if you can use only RxJS operators, and get the output to match the output for === Custom Observable === above. That's what I'm looking for. Thanks!
You should be able to use multicast with a subject factory instead of a subject. Cf. https://jsfiddle.net/pto7ngov/1/
(function(){
console.log('=== RefCounted Observable ===');
var base$ = new Rx.ReplaySubject(1);
var listen$ = base$.map(work).multicast(()=> new Rx.ReplaySubject(1)).refCount();
var subscription1 = listen$.subscribe(x => console.log('Subscription 1: ' + x));
base$.next(1);
base$.next(2);
base$.next(3);
console.log('Unsubscribe');
subscription1.unsubscribe();
base$.next(4);
base$.next(5);
base$.next(6);
console.log('Resubscribe');
var subscription2 = listen$.subscribe(x => console.log('Subscription 2: ' + x));
base$.next(7);
base$.next(8);
})();
This overload of the multicast operator serves exactly your use case. Every time the observable returned by the multicast operator completes and is reconnected to, it creates a new subject using the provided factory. It is not very well documented though, but it basically replicates an existing API from Rxjs v4.
In case I misunderstood or that does not work let me know,

RxJS 5: Can I create a `BehaviorObservable`?

I'm creating a class for my app that keeps an internal BehaviorSubject. Is there a way I can expose a corresponding 'BehaviorObservable', which pipes through the values of the subject, but only has the 'read-only' stuff of the Observable interface? The .next(), .error() and .complete() methods should only be available internally.
It's not just a matter (I believe) of using Observable.create() to pipe the subject's values through. Users of my API should .subscribe() to the exposed observable, and then immediately get a callback for the stored current value.
I may be able to hack something together, but I'm sure I'm just missing something that RxJS can already do.
Are you by any chance looking for the .asObservable() method - which existed in Rxjs v4. Don't know if that is still the case in Rxjs v5 though.
According to this, the method should have been included in release 5.0.0-beta.2. Also I quote here their work-around if the functionality is not there :
You can get the same functionality by creating an observable with the private subject's subscribe function:
> const subj = new rx.Subject();
> const exposed = new rx.Observable(fn => subj.subscribe(fn));
For more details about subjects' semantics, you can have a look here.

Categories

Resources