Reputation: 2225
I am looking for a way to return an Observable from another Observable. I found that you can do that using pipe and map operators, but it does not seem to be working for me. What am I doing wrong ?
I am using Angular 9.1.12 and rxjs ~6.5.4.
Example: Service1
import { Observable, of } from 'rxjs';
export class Service1 {
test(): Observable<string> {
console.log(1);
return of('hey');
}
}
Service2
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
export class Service2 {
constructor(private service1: Service1) {}
test(): Observable<string> {
return this.service1.test().pipe(
map((value: string) => {
console.log(2);
return value;
})
);
}
}
Component
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
export class Component implements OnInit {
constructor(private service2: Service2) {}
test(): Observable<void> {
return this.service2.test().pipe(
map((value: string) => {
console.log(3);
}));
}
}
}
The console will only output 1
.
Upvotes: 0
Views: 3323
Reputation: 31125
There are two types of observables: hot and cold. I am not going into hot observables since it doesn't have anything to do with the question. You could find more information here
Cold observables - as the name implies - do not start processing it's inner statements until it is subscribed to. So in this case when you subscribe in the component, it would trigger all the observables upto to the inner most of('hey')
.
export class Component implements OnInit {
constructor(private service2: Service2) {}
ngOnInit() {
this.test().subscribe();
}
test(): Observable<void> {
return this.service2.test().pipe(
tap((value: string) => console.log(value))
);
}
}
One more thing to note here, you were using map
operator in the component without a return statement. In that case, it would return undefined
. map
is generally used to transform the incoming statements. tap
operator would be a better fit here. It's used to perform side-effects.
Futhermore, open subscriptions do not close unless an error is emitted or explicitly completed. So it's always a good idea to close open subscriptions after they are used.
Eg. in Angular it's common practice to close it in the ngOnDestroy
hook so that it's closed when the component is closed.
export class Component implements OnInit, OnDestroy {
sub: Subscription;
constructor(private service2: Service2) {}
ngOnInit() {
this.sub = this.test().subscribe();
}
test(): Observable<void> {
return this.service2.test().pipe(
tap((value: string) => console.log(value))
);
}
ngOnDestroy() {
if (!!this.sub)
this.sub.unsubscribe();
}
}
There are more elegant ways to close open subscriptions. See here: https://stackoverflow.com/a/60223749/6513921
Upvotes: 1
Reputation: 8022
Just as functions only run the code inside when you invoke them, Observables
only run their code when you you subscribe to them.
You see 1
output because you invoke this.service1.test()
.
You don't see 2
or 3
because you never subscribe to those Observables
.
export class Component implements OnInit {
constructor(private service2: Service2) {}
test(): void {
this.service2.test().pipe(
map(_ => console.log(3))
).subscribe();
}
}
Upvotes: 1
Reputation: 2916
That's reasonable cause you never subscribe to the observables
so they never actual emitted or run.
You should subscribe on the component like this.
this.test().subscribe();
I have created a stackblitz to play around.
PS: Be aware that you have to unsubscribe when needed also. If you are not familiar with these concepts I suggest you to read that article.
Upvotes: 3