RxJS Keep Subject/Observable Subscription Active After Error - rxjs

Is there a way to prevent a subscription to a stream (either observable or subject) from completing after calling the error function on the observable/subject?
var subject = new Rx.Subject();
subject.subscribe(function(){alert("OnNext")}, function(){alert("OnError")});
var next = document.querySelector("#next").addEventListener('click', function(){
subject.next();
})
var error = document.querySelector("#error").addEventListener('click', function(){
subject.error();
});
From what I understand, calling subject.error() completes the event stream. So keeping the observable open would break the contract of the Observable object. So how can I handle the lifecycle of the observable in such a way that would recreate the subscription after an error occurs? Here is a plnkr demonstrating the behavior.

Simply said, you can't. At least not with Subjects.
Subjects have internal state and when they emit or receive error notification they mark themself as stopped and will never ever emit anything.
Otherwise, you could use catch() or retry() operators that resubscribe to their source Observable but this wouldn't help you when using Subjects.

Related

Observable.forEach what happened to error and completed callback?

I saw an interesting video from 2018 about rxjs and observables
and I was wondering what happened to Observable.forEach
It seems to have been changed quite a lot.
What happened to return type subscription? Now it is a Promise and forEach does not accept
any callback functions for handling errors or completed event?
[
image from https://app.pluralsight.com/library/courses/asynchronous-javascript-rxjs-observables/table-of-contents
Observable.forEach won't emit any next after an error since RxJS 6.3.0.
Observable: forEach will no longer next values after an error (b4bad1f)
You can handle error and complete notifications by then() or catch() because forEach returns a Promise.
range(1, 10).forEach(console.log)
.then(() => console.log('Promise resolved'))
.catch(() => console.log('Promise rejected'));
But yes, there's no way to unsubscribe when using forEach until the source errors or completes.

Should I unsubscribe after a complete?

I have a quick question about observable.
I have the following observable:
getElevation(pos: Cartographic): Observable<Cartographic> {
return new Observable(observer => {
const promise = Cesium.sampleTerrain(this.terrainProvider, 11, Cesium.Cartographic(pos.longitude, pos.latitude))
Cesium.when(promise, (updatedPositions) => {
observer.next(updatedPositions);
observer.complete();
});
});
}
In a component I have:
this.service.getElevation(value).subscribe((e) => {});
My question is, this is a one shoot observable, so I complete just after, is the complete automatically close the subscription? or, do I also have to do this:
const sub = this.service.getElevation(value).subscribe((e) => {sub.unsubscribe();});
In your case you don't need to unsubscribe.
All Observers will automatically be unsubscribed when you call complete. That said, you may want to implement your consuming (component) code do handle the possibility that the implementation of the service may change in the future.
You could do this by using the take operator which will unsubscribe after the first value is emitted:
this.service.getElevation(value).pipe(take(1)).subscribe((e) => {});
You should not unsubscribe in a subscription, it the observable emits instantly then sub is undefined.
If you want a self unsubscribing observable you can use takeUntil
finalise = new Subject();
this.service.getElevation(value).pipe(takeUntil(finalise)).subscribe((e) => {
finalise.next();
finalise.complete();
});
Brief note:
Try to control the subscription with operators such as takeUntil.
You don’t need to unsubscribe yourself if the sender(Subject) completes.
For your case, since the sender returned by getElevation function completes itself after emitting a value one time, you don’t need to either use any operator or unsubscribe yourself to unsubscribe it.
All you have to do: this.service.getElevation(value).subscribe((v) => // do what you want);

RxJS unsubscribe doesn't send complete notification

I don't know if it's by design, but why when I call unsubscribe on observable, it doesn't send the complete notification? For example:
const sub = timer(1000).subscribe({
complete() {
// this isn't being called
}
});
sub.unsubscribe();
because unsubscribe, unsubscribes you from the event, which means that I am not interested with the result anymore. In which case you cannot expect to fire complete event.
Unsubscribe Disposes the resources held by the subscription. May, for
instance, cancel an ongoing Observable execution or cancel any other
type of work that started when the Subscription was created.

Difference between RxJS5 subscription and observer

I see some question/answers relating to Rx Subscriptions/Observers but they may be for older versions of Rx and also not for RxJS, which may conform to a different API.
I was under the impression that subscriptions/subscribers and observers were all the same. If you look at the docs, they are in different adjacent sections, but seem to be exactly the same:
Observer:
http://reactivex.io/rxjs/manual/overview.html#observer
Subscription:
http://reactivex.io/rxjs/manual/overview.html#subscription
what the heck is the difference? Can someone given an example with a practical difference between the two?
An Observer is a consumer of values delivered by an Observable.
So basically the observer receives the values emitted by a stream.
A Subscription is an object that represents a disposable resource, usually the execution of an Observable.
A subscription is basically just a "fact" that a certain observer currently receives data, if you unsubscribe a subscription, both the stream and the observer will still exist, they are just not connected any more.
A real-world metaphor mixed with pseudo-code: Newspaper
Stream: This would be the production-chain of the newspaper (involing the publishing company creating the content and the printing house printing the paper)
const newsPaper$ = Observable.interval(DAILY)
.switchMapTo(date => publishingCompany.createContent(date))
.switchMapTo(content => printingHouse.printPaper(content))
.publish()
.refCount();
Observer: This would be the reader/recipient, that guy with a bathrobe that picks up the newspaper in his front-yard every morning to read it.
const bathrobeGuy = {
next: newsPaper => readPaper(newsPaper),
error: errorMsg => complainAbout(errorMsg), // the bathrobe guy will be so angry, the he unsubscribes the paper
complete: () => subscribeToDifferentNewsPaper()
}
Subscription: This is the news-paper-subscription - the delivery-boy throwing the newspaper into each front-yard every morning.
// this will activate the "delivery boy"
const paperSubscription = newsPaper$.subscribe(bathrobeGuy);
Unsubscribing: When the bathrobe-guy decides to not want the paper any more, he can unsubscribe the paper and the delivery-boy will not deliver any paper any more. However the observer(the bathrobe-guy) and the newspaper-production still exist, but they have simply no relationship any more.
paperSubscription.unsubscribe();
An Observer is an object with a set of callbacks that are executed when you subscribe to an Observable. In other words, when you call subscribe you pass an object of type Observer. Even when you only pass a callback, internally rxjs is creating an Observer with your callback as the next property. Other properties are error and complete.
A Subscription is the return type of the call subscribe, and its only purpose is to be able to call subscription.unsubscribe() in order to not listen to that subscription anymore. The Observer functions (next, error, complete) will no longer be called.
var myObserver = {
next: (val) => {},
error: (err) => {},
complete: () => {}
};
var mySubscription: Subscription = myObservable.subscribe(myObserver);
// then, if later you want to unsubscribe:
mySubscription.unsubscribe()

checking if rxjs observable is complete

I have a class that when instantiated makes some web service calls, pseudo code below:
Rx.Observable.fromPromise(jQuery.getJSON('https://api.github.com/users'))
.flatMap(function () {
return Rx.Observable.fromPromise(jQuery.getJSON('https://api.github.com/users'));
});
The same class is listening for an onclick event.
When this even is triggered, if the original web service calls are complete: do something
If they are not complete, wait for them to complete, before doing something.
I was wondering how to achieve this with the rxjs approach? rather than setting variables and using if statements.
I would refer to this as an Asynchronous Gate.
These are actually pretty easy to do with Rx.
You will need to cache the web service calls observable sequences.
Then in other calls that are predicated on these being complete, you simply flatMap off their results.
As these are from Promises I believe the result is retained for late subscribers, but if not then you just need to replay(1) the sequences.
So in psudeo code
var startUpData = Rx.Observable.fromPromise(jQuery.getJSON('https://api.github.com/users'))
.flatMap(function () {
return Rx.Observable.fromPromise(jQuery.getJSON('https://api.github.com/users'));
});
var events = Rx.Observable....//Your event wired up here.
//When an event
events
.flatMap(function(evt){
//Wait until the startUpData yeilds, but pass on the evt data instead.
return startUpData.map(function(){ return evt;})
//do something here knowing that your event has fired, but the web services have also completed.
.subscribe();
You can see Matt Barrett explain an Async gate in this video at about 51minutes in to this video - https://youtu.be/Tp5mRlHwZ7M?t=51m30s
You may also want to consider the switch operator incase you don't want overlapping events
I believe withLatestFrom or combineLatest will do what you're asking.
Depending on if you wish to only allow the button to be clicked once with the data provided from the service you could use withLatestFrom. If you wish to allow the button to continued to be clicked using the data previously provided by the service you can use combineLatest
const futureEvent$ = Rx.Observable.timer(3000);
const btnClick$ = Rx.Observable
.fromEvent(document.querySelector('button'), 'click');
const futureAndBtnClick$ = futureEvent$.combineLatest(btnClick$);
futureAndBtnClick$.subscribe(x => console.log('click + future stuff happened'));
jsbin example

Resources