Hey there I've an update function call with nested subscribe, interdependent, just that on the success of the parent call subscribe the inner one
updateSelectedScopes() {
let params: any = {
userInterface: this.userInterface,
targetId: this.assessmentId,
targetUserId: this.currentUser['userId'],
};
this.lockService.acquireLock(params).subscribe(lockingDetails => {
if (lockingDetails) {
let payload = this.consolidatedPayload
this.assessmentService.updateSelectedScopes(payload).subscribe(res => {
if (res) {
this.releaseLock(
this.assessmentId,
this.currentUser['userId'],
this.userInterface
);
this.modalService.dismissAll();
if ((this.assessmentStatus === this.appConstants.CERTIFIED ||
this.assessmentStatus === this.appConstants.UN_CERTIFIED ) ||
this.assessmentStatus === this.appConstants.ASSESSMENT_FLAGGED) {
this.assessmentService.initiateWorkflow(this.assessmentId).pipe(take(1)).subscribe(() => {
this.router.navigate([this.appConstants.VIEW_ASSESSMENT_URI], { skipLocationChange: true, queryParams: { 'assessmentId': this.assessmentId, assessment: this.assessmentDetailsObj['assessmentName'], 'updateFlag': true } });
}, (error) => {
this.modalService.dismissAll();
this.openAlert(error.error);
});
} else {
this.router.navigate([this.appConstants.VIEW_ASSESSMENT_URI], { skipLocationChange: true, queryParams: { 'assessmentId': this.assessmentId, assessment: this.assessmentDetailsObj['assessmentName'], 'updateFlag': true } });
}
}
}, error => {
this.modalService.dismissAll();
this.releaseLock(
this.assessmentId,
this.currentUser['userId'],
this.userInterface
);
this.openAlert(error.error);
});
}
}, error => {
this.openLockDialog(
error.error,
this.currentUser['userId'],
this.assessmentId,
);
})
}
Can I optimize this function(with nested subscribes) with switchMap/mergeMap/flatMap
, in order to avoid this ugly nesting subscribes. Any help would be great
Ususally in similar situation you should concatenate a series of Observables using the concatMap
operator.
concatMap
ensures that as soon as the upstream Observable notifies something, the following Observable is "executed".
In your logic it seems there is also some "non reactive" logic, specifically in the case of errors. To manage this situation you can use the catchError
operator.
The idea is the following
// you have 3 functions that return observables
const doStuff: (param: string) => Observable<string>;
const doSomethingElse: (param: string) => Observable<string>;
const doSomeMoreStuff: (param: string) => Observable<string>;
doStuff("a param").pipe(
concatMap((result) => {
return doSomethingElse(result).pipe(
catchError((error) => {
// do something with error that may occur in doSomethingElse
return of("")
})
)
}),
concatMap((result) => {
return doSomeMoreStuff(result).pipe(
catchError((error) => {
// do something with error that may occur in doSomeMoreStuff
return of("")
})
)
}),
catchError((error) => {
// do something with error that may occur in doStuff
return of("")
})
)
.subscribe()
In your case, your code could be refactored more or less like this (I do not have a working example and the code is a bit complex, so I could not test the following snippet and therefore there may be errors - it is meant just to give an idea)
this.lockService.acquireLock(params).pipe(
concatMap(lockingDetails => {
if (lockingDetails) {
let payload = this.consolidatedPayload
return this.assessmentService.updateSelectedScopes(payload).pipe(
catchError(
(error) => {
this.modalService.dismissAll();
this.releaseLock(
this.assessmentId,
this.currentUser['userId'],
this.userInterface
);
this.openAlert(error.error);
return of(null)
}
)
)
}
return of(null)
}),
concatMap(res => {
return this.assessmentService.updateSelectedScopes(payload).pipe(
catchError(
(error) => {
this.modalService.dismissAll();
this.openAlert(error.error);
return of(null)
}
)
)
}),
catchError(
(error) => {
this.openLockDialog(
error.error,
this.currentUser['userId'],
this.assessmentId,
);
}
)
)
.subscribe()
This article can give you some inspiration for similar use cases.