RXJS - subscribing to the Server side event on first subscribe, unsubscribe on last unsubscribe and resubscribe on next subscribe - rxjs

I have a front end with Server Side events and multiple places where I want to subscribe to it.
I want to subscribe to the server-side event once, and also unsubscribe when no one observes it.
I'm not sure how to accomplish that with rxjs.
Here's a walkthrough of what I want:
X subscribes
result => Connect to Server Side Event
Y Subscribe
result => Listen to the same server-side event
X unsubscribe
result => nothing
Y unsubscribe
result => Disconnect from Server Side Event
Z subscribes
result => Connection to Server Side Event again
I've played with defer, and new Observable etc... but feel like I'm not on the right path

Related

Process a stream of events after a Start event and return to listen when an End event occurs

The scenario I am working on is composed by 3 Observables.
StartObs: this Observable emits when I need to start a sequence of processings - the data emitted is a processID of the process I need to fulfill
DoStuffObs: this Observable emits commands upon which I have to do something - I want to start listening to such Observable just after StartObs has emitted and I need the processID of the process to perform my duties with the function doTheWork(command, processId)
EndObs: this Observable emits when I have to end the processing of a certain processID and have to go back to listen to the next emission of StartObs
So basically is: Start, DoStuff until End and then go back to listening to the next Start.
It is also guaranteed that after one Start comes sooner or later one End and that it is not possible to have 2 Start without an End in between or 2 End without a Start in between.
The first 2 steps can be achieved via switchMap, like
StartObs
.switchMap(processId => DoStuff.map(command => ({command, processId})))
.tap(data => doTheWork(data.command, data.processId))
What is not clear to me is how to deal with EndObs.
The option of using takeUntil does not work, since I do not want to complete the chain started with StartObs since I have to go back to listening for the next process to start.
Actually, I think takeUntil() is the best choice here in combination with repeat().
StartObs
.switchMap(processId => DoStuff.map(command => ({command, processId})))
.tap(data => doTheWork(data.command, data.processId))
.takeUntil(EndObs)
.repeat();
When the chain completes with takeUntil() it will immediately resubscribe thanks to repeat() and the whole process will start all over again.

Cancel previous requests and only fire the latest request with redux observable

So I have use case where I update the api request when the map is moved - but it could generate several rapid fire requests with small map movements - and I want to cancel all the inflight requests except for the last one. I can use debounce to only send requests after a delay. However I still want to cancel any old requests if they happen to still be in process.
const fetchNearbyStoresEpic = action$ =>
action$.ofType(FETCH_NEARBY_STORES)
.debounceTime(500)
.switchMap(action =>
db.collection('stores')
.where('location', '<=', action.payload.max).
.where('location', '>=', action.payload.min)
.map(response => fetchNearbyStoresFulfilled(response))
.takeUntil(action$.ofType(FETCH_STORES_CANCELLED))
);
I see that you can use takeUntil but you need to explicitly fire a cancel action. I see in the docs that switchMap will take the latest and cancel all the others - do I have to implement a cancel interface in my api call? In this case it would be a firebase query to firestore.
From a comment I made in a GitHub issue:
Because they have a time dimension, there are multiple flattening strategies for observables:
With mergeMap (which has flatMap as an alias), received observables are subscribed to concurrently and their emitted values are flattened into the output stream.
With concatMap, received observables are queued and are subscribed to one after the other, as each completes. (concatMap is mergeMap with a concurrency of one.)
With switchMap, when an observable is received it's subscribed to and any subscription to a previously received observable is unsubscribed.
With exhaustMap, when an observable is received it's subscribed to unless there is a subscription to a previously received observable and that observable has not yet completed - in which case the received observable is ignored.
So, like Mark said in his answer, when switchMap receives a subsequent action, it will unsubscribe from any incomplete request.
However, the request won't be cancelled until the debounced action makes it to the switchMap. If you want to cancel any pending requests immediately upon another move - rather than wait for the debounce duration - you can use takeUntil with the FETCH_NEARBY_STORES action:
const fetchNearbyStoresEpic = action$ =>
action$.ofType(FETCH_NEARBY_STORES)
.debounceTime(500)
.switchMap(action =>
db.collection('stores')
.where('location', '<=', action.payload.max).
.where('location', '>=', action.payload.min)
.map(response => fetchNearbyStoresFulfilled(response))
.takeUntil(action$.ofType(FETCH_NEARBY_STORES))
);
That should effect the immediate unsubscription from a request upon another move. (Off the top of my head, I cannot recall the behaviour of action$ in redux-observable. It's possible that you might need to append a skip(1) to the observable passed to takeUntil. Try it and see.)
And, as Mark mentioned, this is predicated on the underlying implementation cancelling the request upon unsubscription.
switchMap will abandon its previous observable when a new emission is send through it. Depending on your underlying HTTP library and if it supports cancellation (Observable aware) this should suffice.
Because no implementation details have been provided in your question you will have to look into fetchNearbyStoresFulfilled to see if it uses an Observable aware http client. If it internally is using promises then no cancellation support is provided.

RxJS - pausing an Observable until second Observable completes,

I have a scenario where 1 observable listens for events, which should then fire another asynchrounous event, and wait before it runs the next item in the source Observable.
The first observable can be triggered much faster than the the async event, and it must wait for the async event to complete before it takes another item from the 1st observable.
So.. essentially I need to set up a 'queue' from the first observable (as I cant lose the data from source 1)
Source 2 should take 1 item at a time from the queue, run it, remove the item from the queue, and go onto the next item in the queue .
src1- --ev1---ev2---ev3----ev4---ev5--ev6---
src2- --ev1------------ev2-------------ev3--------ev4-------ev5------ev6
--------------async-----------async---------async------async------asyc
I was looking at the RX docs and it seems that pausibleBuffered could be a solution but I noticed it has been removed in RX5, which is what I am using. Can someone give advice as the right way to accomplish this ?
Thanks!
You can use mergeScan to run async operations one by one because it needs the previous async operation’s result to run an async operation.
const src2 = src1.mergeScan((_, value) => doSomething(value));
http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-mergeScan

Why does Rxjs unsubscribe on error?

In short:
How to proceed listening after an error in stream without putting a .catch before every .subscribe?
If you need more details they are here:
Lets assume I have a Subject of current user or null. I get the data from API sometimes and send to the Subject. It updates the view accordingly.
But at some point error occurs on my server and I want my application to continue working as before but notify some places about the error and KEEP listening to my Subject.
Initially I thought that if I just do userSubject.error(...) it will only trigger .catch callback and error handlers on subscribes and skip all success handlers and chains.
And if after I call userSubject.next(...) all my chains and subscribers will work as before
BUT unluckily it is not the case. After the first uncaught .error it unsubscribes subscribers from the stream and they do not operate any more.
So my question: Why???
And what to do instead if I want to handle null value normally but also handle errors only in some places?
Here is the link to RxJs source code where Subscriber unsubscribes on error
https://github.com/ReactiveX/rxjs/blob/master/src/Subscriber.ts#L140
Rx observables follow the grammar next*(error|complete)?, meaning that they can produce nothing after error or complete notification has been delivered.
An explanation of why this matters can be found from Rx design guidelines:
The single message indicating that an observable sequence has finished ensures that consumers of the observable sequence can deterministically establish that it is safe to perform cleanup operations.
A single failure further ensures that abort semantics can be maintained for operators that work on multiple observable sequences.
In short, if you want your observers to keep listening to the subject after a server error has occurred, do not deliver that error to the subject, but rather handle it in some other way (e.g. use catch, retry or deliver the error to a dedicated subject).
Every Observable emits zero or more next notifications and one error or complete but never both.
For this reason, Subjects have internal state.
Then it depends how you construct your chain. For example you can use retry() to resubscribe to its source Observable on error.
Or when you pass values to your Subject you can send only next notifications and ignore the other two:
.subscribe(v => subject.next(v));
Or if you want to throw error when the user is null you can use any operator that captures exceptions and sends them as error notifications. For example like this:
.map(v => {
if (v === null) {
throw new Error("It's broken");
}
return v;
})
Anyway it's hard to give more precise advice without any code.

RxJs: is it good practice to unsubscribe from Observables when navigating away?

I am just getting into RxJs and Observables in general. I grasped the idea that often you can create "self-contained" Observable by utilizing "takeUntil()".
In one online-course I am watching the teacher says "I did not unsubscribe from anything in 10 years because I always use takeUntil() to create ending streams of events". This is his example:
var getElementDrags = elmt => elmt
.mouseDowns.map(() => document.mouseMoves.takeUntil(document.mouseUps))
.concatAll();
That is very nice for the "inner" Observables. But the one outer Observable on "mousedown" never really gets unsubscribed from...
Do we still need to unsubscribe from those?
Is it still good practice to unsubscribe/dispose when the user leaves the page?
In example you have - you are not subscribing to anything... RxJS is lazy, and it will subscribe to mouseDowns only when you will subscribe to resulting observable, and of course - it will unsubscribe from underlining observables when you will unsubscribe from resulting observable.
But, generally - yes, it is a good practice to unsubscribe when you are subscribing to something… But - while using RxJS, typically you will not need to subscribe manually, and when you need - chances are that you need subscription while app is running(so no need to unsubscribe).
The only exceptions are - when you are developing own operators, or connecting to something outside…
For example if you have react component and use life-cycle hocks for subscription to updates on mount, and unsubscribe when un-mounting.
Here is my library for that purpose https://github.com/zxbodya/rx-react-container - it combines observables, subjects and react component into new observable with renderable items...
const app$ = createContainer(
App, // react component
{totalCount$}, // observables with data
{plusOne$, minusOne$} // observers for user actions
);
const appElement = document.getElementById('app');
const appSubscription = app$.forEach(renderApp=>render(renderApp(), appElement));
In result you have only one subscription to manage for a whole application(appSubscription), and no need to unsubscribe - since it is used while app is running.
The same thing, about routing and unsubscribe when navigating away - in simplified case you will have just flatMapLatest over observable with current location, that will return observable(like app$ above) for each location… And again you do not need to subscribe/unsubscribe manually - flatMapLatest will do it internally.

Resources