Reputation: 143
I am trying to subscribe two observable and get the values to store in an array object. It is working ok but my problem is it iterates three times which i don't understand. I am doing this in a service to create a new service. Below is the code for example. I am also wondering can i use promise instead of observable in angular?. Or can i convert the observable into promise and resolve after I get the value ? Thank you for your help
addEmployeeData() {
const employeeObservable = this.apiService.getEmployeeDataObservable();
employeeObservable.subscribe({
next: (res: any) => {
let employee = res;
const paramsObservable = this.apiService.getParamsObservable();
pageParamsObservable.subscribe({
next: (pageParams: any) => {
Upvotes: 1
Views: 7459
Reputation: 31135
switchMap
) to switch from one observable to another that depend on each other.addEmployeeData() {
this.apiService.getEmployeeDataObservable().pipe(
switchMap(_ => {
let employee = res; // why is this needed though?
return this.apiService.getParamsObservable();
})
).subscribe({
next: (pageParams: any) => {
// handle response
},
error: (error: any) => {
// handle error
}
);
}
If the observables are not related to each other (eg. if the second request does not depend on the result of first request) you could use functions like RxJS forkJoin
or combineLatest
to trigger the observables in parallel.
See my post here for a quick comparison of mapping operators and combination functions.
It's good practice to close any open subscription in the ngOnDestroy
so they don't persist and lead to potential memory leaks. There are multiple ways to close the subscription.
3.1 unsubscribe
- Assign the subscription to a member variable and call unsubscribe()
on it in the ngOnDestroy
hook.
3.2 take(1)
- Close the subscription after the emission of first notification.
3.3 first()
- Similar to take(1)
but takes additional predicate and emits error if none of the emissions matched the predicate. See here for take(1)
vs first()
.
3.4. takeUntil()
- Use additional multicast to close multiple open subscriptions with a single emission. Most preferred way for it's elegance. See here for more info.
Note: Observables returned by Angular HttpClient complete after the first emission without any of the above mentioned operations. So in most cases they aren't required.
Upvotes: 2
Reputation: 1384
Since you have 2 Observables and you are doing the actual logic only after you subscribe to the second observable, i am assuming without the second observable data, you are not implementing any logic.
For this you have 2 options to go about.
(OR)
combineLatest
operator and also use Subject variable to destroy the subscription after its done. This is how you do this:The advantage of this method over mergeMap
is you will have only one subscription and one unsubscription whereas in mergeMap
you will have 2.
MergeMap will subscribe to the first variable and if its successfully subscribed then only it will subscribe to the second subscription.
In combineLatest
it will subscribe irrespective of the data that is coming.
That is kind of an advantage and a disadvantage depending on how you use it.
And If you want only the first value, please use take(1)
operator to stop it from further subscribing
const employeeObservable = this.apiService.getEmployeeDataObservable();
const paramsObservable = this.apiService.getParamsObservable();
destroy$: Subject<any> = new Subject(); // This is used to destroy the subscription later
// Combine the observables and destroy once done in 1 shot.
combineLatest(employeeObservable,paramsObservable)
.pipe(
takeUntil(this.destroy$),
take(1)
)
.subscribe(
([employeeObservableData, paramsObservableData]) => {
if (employeeObservableData && paramsObservableData) {
// do your logic here
}
}
);
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
Upvotes: 1
Reputation: 330
Yes, you can work with Observables as with Promises:
async asyncAddEmployeeData(): Promise<any> {
return this.apiService.getEmployeeDataObservable()
.pipe(
mergeMap(employeeData => this.apiService.getParamsObservable()
.pipe(
tap((paramsData): void => {
// There is available data
// from apiService.getEmployeeDataObservable()
// as employeeData variable
// and data from apiService.getParamsObservable()
// as paramsData.
// You can do in tap function all the same
// as in next in the subscribe.
}),
)
),
)
.toPromise();
}
And use it like here:
async ngOnInit(): Promise<void> {
// ngOnInit just for example.
const someVariable = await this.asyncAddEmployeeData();
}
But regular way of using Observable looks like this:
addEmployeeData(): Observable<any> {
return this.apiService.getEmployeeDataObservable()
.pipe(
mergeMap(employeeData => this.apiService.getParamsObservable()
.pipe(
tap(paramsData => {
// There is available data
// from apiService.getEmployeeDataObservable()
// as employeeData variable
// and data from apiService.getParamsObservable()
// as paramsData.
}),
)
),
take(1), // Just if you need only first value, if not, please, remove this string.
);
}
And subscription:
ngOnInit(): void {
// ngOnInit just for example.
this.subscription = this.addEmployeeData().subscribe();
}
Don't forget unsubscribe to avoid of memory leaks:
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
Upvotes: 2
Reputation: 196
Try to create a subscription and unsubscribe on onDestroy.
// import
import { Subscription } from 'rxjs';
subscription: Subscription;
ngOnInit() {
this.subscription = // employeeObservable.subscribe()
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
Upvotes: 0