How To Write Nested Subscribe In Cleaner Way?
Solution 1:
To effectively handle nested subscribes, you should use one of the "Higher Order Mapping Operator", which do a few nice things for you:
- maps the incoming value to another observable
- subscribes to it, so its values are emitted into the stream
- manages unsubscribing from these "inner subscriptions" automatically
In this case, switchMap
is a good choice because it will only allow a single inner subscription at a time, so whenever myService.search(name)
is called a new inner subscription for myService.create(name)
is created, and the previous one is automatically unsubscribed.
@Rafi Henig's answer shows a good example of what this could look like.
- Notice the use of
.pipe()
. You can define transformations to your observable output using pipeable operators without actually subscribing.
I would suggest you don't subscribe in your testMethod()
, but rather return an observable. Also, let's give "testMethod()" a more meaningful name for further discussion: "getPerson()".
getPerson(name: string): Observable<Person> {
returnthis.myService.search(name).pipe(
switchMap(result => {
returniif(
() => result,
of(result),
this.myService.create(name).pipe(
map(({ id }) => ({ id, name }))
)
)
}),
tap(person =>this.currentPerson = person)
);
}
console.log shows undefined. Any tips how to fix it?
1firstMethod() {
2this.getPerson(name)
3console.log(this.currentPerson)
4 }
The reason for the undefined
is because the code is asynchronous. Line 2 is executed, then line 3 immediately after, but the async work hasn't been finished yet, so this.currentPerson
hasn't been set yet.
since our getPerson()
method now returns an observable, we can subscribe and do your console.log()
inside the subscribe:
1firstMethod() {
2this.getPerson(name).subscribe(
3 () => console.log(this.currentPerson)
4 )
5 }
To simplify, we don't even need this.currentPerson
anymore, because the person is emitted through the stream!
1firstMethod() {
2this.getPerson(name).subscribe(
3 person => console.log(person)
4 )
5 }
And since you want to...
learn how to write code using it in clean way
I think think cleanest way would probably be to define your "person result" as an observable and ditch this.currentPerson
.
person$ = this.getPerson(name);
So now you have this.person$
which can be subscribed to and will always have the current value of person. No need to "manually" update this.currentPerson
.
Well... almost. We need to consider what happens when the search term changes.
Let's assume the search term "name" is coming from a form control input.
When using Reactive Forms the input value is an observable source, so we can define our person$
from the search term:
searchTerm$ = this.searchInput.valueChanges();
person$ = this.searchTerm$.pipe(
switchMap(searchTerm => this.getPerson(searchTerm))
);
getPerson(name: string): Observable<Person> {
returnthis.myService.search(name).pipe(
switchMap(result => {
return iif(
() => result,
of(result),
this.myService.create(name).pipe(
map(({ id }) => ({ id, name }))
)
)
})
);
}
Notice we've defined two different observables, but we haven't subscribed yet! Now, we can leverage the async
pipe in our template to handle the subscription, keeping our component code nice and simple.
<p *ngIf="person$ | async as person">
We found {{ person.name }} !
</p>
I know this has gotten a bit long winded, but I hope you see how its possible to transform output using pipeable operators and how you can define one observable from another.
Solution 2:
Use switchMap to return a new Observable based of the the value of result
using IIF operator as demonstrated below:
this.myService.search(name)
.pipe(
switchMap(result => {
returniif(
() => result,
of(result),
this.myService.create(name).pipe(map(({ id }) => ({ id, name })))
)
})
)
.subscribe(person => {
})
Or optionally:
this.myService.search(name)
.pipe(
switchMap(result => {
if (result) returnof(result);
elsethis.myService.create(name).pipe(map(({ id }) => ({ id, name })));
})
)
.subscribe(person => {
})
Post a Comment for "How To Write Nested Subscribe In Cleaner Way?"