RxJS Is Observer and Subscriber the same thing? - rxjs

From this documentation:
RxJS introduces Observables, a new Push system for JavaScript. An Observable is a Producer of multiple values, "pushing" them to Observers (Consumers).
Subscribing to an Observable is analogous to calling a Function.
To invoke an Observable we should call the subscribe() function from the Observable object itself and pass the observer as the consumer of the data delivered by the observable like:
observable.subscribe( { /*this is an observer*/ } );
Also this documentation says:
What is an Observer? An Observer is a consumer of values delivered by an Observable. Observers are simply a set of callbacks, one for each type of notification delivered by the Observable: next, error, and complete. The following is an example of a typical Observer object:
On the other hand the first documentation says:
The Observable constructor takes one argument: the subscribe function.
The following example creates an Observable to emit the string 'hi' every second to a subscriber.
import { Observable } from 'rxjs';
const observable = new Observable(function subscribe(subscriber) {
const id = setInterval(() => {
subscriber.next('hi')
}, 1000);
});
When calling observable.subscribe with an Observer, the function subscribe in new Observable(function subscribe(subscriber) {...}) is run for that given subscriber. Each call to observable.subscribe triggers its own independent setup for that given subscriber.
So the entity Subscriber is just the argument passed into the subscribe function when creating a new Observable? If not who is the subscriber?
Are Observers and Subscribers the same entity? as mentioned in this documentation
Why isn't the code that invokes observable.subscribe({observer as call backs}) the subscriber of the observable? Like the consumer of a function's return value is the code that makes the function call.
COMPLETE DESIGN PATTERN EXPLANATION

Observer
const observer = {
next: v => /* code for next callback*/,
error: err => /* code for error callback*/,
complete: () => /* code for completion callback*/
}
Subscription
const subscription = {
unsubscribe: () => /* code for unsubscribe callback */
}
Observable
const observable1 = from([1,2,3,4,5]);
const observable2 = of(1,2,3,4,5);
const observable3 = new Observable(observer => {
observer.next(1);
observer.next(2);
observer.next(3);
observer.next(4);
observer.next(5);
observer.complete();
return { // return a subscription
unsubscribe: () => /* code for unsubscribe callback */
};
});
Subscribe and use the Returned Subscription
// Store a subscription
const subscription = observable3.subscribe(observer);
// Invoke the unsubscribe callback defined by the observable.
subscription.unsubscribe();
Okay. Then What is a Subscriber?
[Subscriber] Implements the Observer interface and extends the Subscription class. While the Observer is the public API for consuming the values of an Observable, all Observers get converted to a Subscriber... Subscriber is a common type in RxJS, and crucial for implementing operators, but it is rarely used as a public API.
Are observers and subscribers the same thing? Kind of, yes? Depends on how concretely you ask the question.
Consider this:
observable3.subscribe({
next: v => /* code for next callback */
});
obsevable3.subscribe(
v => /* code for next callback */
);
The first is an object with only one observer property defined. The second is simply a lambda function. They both end up generating basically the same subscriber.

Related

Subject-like RxJS Observable that transparently pipes through flatMap

When using Dependency injection in Angular I often need to subscribe to an observable that I haven't yet created!
I often end up using something like this:
// create behavior subject OF Observable<number>
const subject = new BehaviorSubject<Observable<number>>(EMPTY);
// subscribe to it, using flatMap such as to 'unwrap' the observable stream
const unwrappedSubject = subject.pipe(flatMap((x: number) => x));
unwrappedSubject.subscribe(s => console.log(s));
// now actually create the observable stream
const tim = timer(1000, 1000);
// set it into the subject
subject.next(tim);
This uses flatMap to 'unwrap' the observable contained in the subject.
This works fine, but frankly it always feels 'icky'.
What I really want is something like this, where the consumer of the subject treats the instance of the Subject as Observable<number> without having to pipe it every usage.
const subject = new UnwrappingBehaviorSubject<number>(EMPTY);
subject.subscribe((x: number) => console.log(x));
// this could use 'next', but that doesn't feel quite right
subject.setSource(timer(1000, 1000));
I'm aware that I could subscribe to the timer and hook it up directly to the subject, but I also want to avoid an explicit subscribe call because that complicates the responsibility of unsubscribing.
timer(1000, 1000).subscribe(subject);
Is there a nice way to achieve this?
The Subject.ts and BehaviorSubject.ts source files get more complicated than I expected. I'm scared I'll end up with horrible memory leaks if I try to fork it.
I think this would be another way to solve it:
foo.component.ts
export class FooComponent {
private futureObservable$ = new Observable(subscriber => {
// 'Saving' the subscriber for when the observable is ready.
this.futureObservableSubscriber = subscriber;
// The returned function will be invoked when the below mentioned subject instance
// won't have any subscribers(after it had at least one).
return () => this.futureObservableSubscription.unsubscribe();
}).pipe(
// You can mimic the Subject behavior from your initial solution with the
// help of the `share` operator. What it essentially does it to *place*
// a Subject instance here and if multiple subscriptions occur, this Subject instance
// will keep track of all of them.
// Also, when the first subscriber is registered, the observable source(the Observable constructor's callback)
// will be invoked.
share()
);
private futureObservableSubscriber = null;
// We're using a subscription so that it's easier to collect subscriptions to this observable.
// It's also easier to unsubscribe from all of them at once.
private futureObservableSubscription = new Subscription();
constructor (/* ... */) {};
ngOnInit () {
// If you're using `share`, you're safe to have multiple subscribers.
// Otherwise, the Observable's callback(i.e `subscriber => {...}`) will be called multiple times.
futureObservable$.subscribe(/* ... */);
futureObservable$.subscribe(/* ... */);
}
whenObservableReady () {
const tim = timer(1000, 1000);
// Here we're adding the subscription so that is unsubscribed when the main observable
// is unsubscribed. This part can be found in the returned function from the Observable's callback.
this.futureObservableSubscription.add(tim.subscribe(this.futureObservableSubscriber));
}
};
Indeed, a possible downside is that you'll have to explicitly subscribe, e.g in the whenObservableReady method.
With this approach you can also have different sources:
whenAnotherObservableReady () {
// If you omit this, it should mean that you will have multiple sources at the same time.
this.cleanUpCrtSubscription();
const tim2 = timer(5000, 5000);
this.futureObservableSubscription.add(tim2.subscribe(this.futureObservableSubscriber));
}
private cleanUpCrtSubscription () {
// Removing the subscription created from the current observable(`tim`).
this.futureObservableSubscription.unsubscribe();
this.futureObservableSubscription = new Subscription();
}

Angular 9 and rxjs - wait for message event after postMessage

I am new to rxjs and not sure how to implement the follow logic. Any suggestion will be appreciated.
Background
I am going to implement the communication between host website and an iframe in it with postMessage. Since postMessage is one-way only, I would like to implement the logic to wait for 'response' by myself when a message is sent from host website to iframe.
I have a sync function called send(message) to invoke the postMessage to send message to iframe. Then I would like to have another function with the follow logic.
public async sendAndWait(message): Promise<responseObj> {
// 1. create an observable to wait to message event with timeout
// my first thought is as follow but I feel like it does not work
// fromEvent(window, 'message')
// .pipe(timeout(timeoutInMs))
// .subscribe(event => {
// console.info(event);
// });
// 2. run `send(message)` function
// 3. do not finish this function until timeout or receive event in the previous subscription.
}
When I use the function, I would like to have
let response = await sendAndWait(message);
Not sure if it is possible to implement? Thank you
You cannot stop code execution in JS (using Async-Await, a Promise object is returned behind the scenes. so that the code is never waiting)
Consider implementing it in the following way:
let response: responseObj;
function main(): void {
sendAndWait(MESSAGE_OBJECT).subscribe(x => response = x)
}
function sendAndWait(message): Observable<responseObj> {
send(message)
return fromEvent(window, 'message')
.pipe(
timeout(timeoutInMs),
first()
)
}
Or optionally returning Promise:
async function sendAndWait(message): Promise<void> {
send(message)
const response = await fromEvent(window, 'message')
.pipe(
timeout(timeoutInMs),
first(),
toPromise()
)
}

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);

Invoke method when no observers for RxJs Subject

How to invoke a method when all the observers have unsubscribed from a subject.
Update
const alphaStore = new BehaviourSubject(0);
observer1 = alphaStore.subscribe(console.log);
observer2 = alphaStore.subscribe(console.log);
And when all of these observers unsubscribe. I want a method to be invoked. Like...
Observer1 unsubscribed
Observer2 unsubscribed
All observers left
What you describe already does the finalize() operator. Better said finalize() calls its callback when the chain disposes which means it's called when all observers unsubscribes, the chain completes or errors.
const subject = new Subject();
const shared = subject.pipe(
finalize(() => console.log('finalize')),
share(),
);
https://stackblitz.com/edit/rxjs-rebfba
When all observers unsubscribe share() unsubscribes from its source which triggers finalize().
Currently there's no way to distinguish why finalize() was invoked. See this issue https://github.com/ReactiveX/rxjs/issues/2823 and examples there on how to do it.
You can create a custom Observable, that will track the subscription count.
Heres a simple example:
let count = 0;
const tracked$ = new Observable(() => {
count++;
return ()=>{
count--;
if (count === 0) {
console.log('I am empty');
}
};
})
And then merge it with Observable that does actual work.
For simplicity sake, lets imagine its just a timer
// const tracked$ = ...
const data$ = Observable.timer(0, 5);
const result$ = data$
.merge(tracked$)
.take(5)
.subscribe(value => console.log('v:', value));
After 5 values were emitted -- it will log I am empty.
Heres a live example (with some rewrite and two subscriptions):
https://observable-playground.github.io/gist/4a7415f3528aa125fb686204041138cb
NOTE: this code uses rxjs-compat notation, which is easier to read. Above linked example uses .pipe notation, which is more common now.
Hope this helps.

Why don't we need to subscribe to an observable here before converting it to a Promise?

My Service class contains code as :
Service.ts
//all imports are done
#Injectable()
export class Service{
constructor(private http: Http) { }
getGoogle():Observable<any> {
console.log("Inside service");
return this.http.get('https://jsonplaceholder.typicode.com/posts/1');
}
}
My page component where I make use of service.
Page.ts
//all imports are done
export class Page1{
constructor(private service: Service, private navCtrl: NavController) { }
async get() {
console.log("inside get method");
const data = await this.service.getGoogle().toPromise();
console.log('The response is' , data);
}
}
I have got the required result, but as to understand the concept of Observables and Observer, my Observable should have an Observer sitting to subscribe.Then why should'nt the code const data = await this.service.getGoogle.subscribe().toPromise() does not work here and shows error that property toPromise() does not exists on type Subscription.
I saw the official resource of toPromise() where I found that it is used with .just().toPromise().Then I found .just() API which states that
The just() method emits its parameter(s) as OnNext notifications, and
after that, it emits an OnCompleted notification.
So It is using the features of subscribe here then why it is not used with .subscribe()?
To get the values from an observable, you'll subscribe() on the observable. This starts the observable and it will send you the values it produces.
If you rather want to use a Promise you can call toPromise() on the observable instead. This will make the observable behave like a regular promise.
Under the covers, toPromise() calls subscribe() on the observable and waits for it to send complete(). When the complete() signal is received, the promise will resolve the last emitted next() signal.
toPromise() looks a bit like this:
myObservable.takeLast().subscribe(value => resolve(value));

Resources