I'm using Angular RxJs subscribe
to make a HttpClient
call and then make another call using the values from the first one. In this case, there's a call to get address object
, and then i make a call using this object. Like this:
@Injectable()
export class AddressService {
constructor(private http: HttpClient) { }
getById(addressId: string, userId: string) {
return this.http.get(BACKEND_URL + 'getAddressById/' + [addressId, userId]);
}
}
export class AddressModalComponent implements OnInit {
constructor(private alertService: AlertService, private addressService: AddressService, @Inject(MAT_DIALOG_DATA) public data: any, private dropdownService: DropdownService)
ngOnInit() {
this.addressService.getById(this.data.id, this.data.userId)
.subscribe(
(address: Address) => {
this.dropdownService.getCidadesBrByEstado(address.name)
.subscribe((cities: BrCity[]) => {
this.cities = cities;
this.address = address;
},
error => console.log(error));
}, error => { this.alertService.error(error);
}
);
}
}
}
I'm trying to avoid multiple Subscribes, there is many like this in my code. I need an Async/Await
approach like Node.js promises
, but using Observables at component level. I'm not very familiar with RxJs commands
... is there a better way to make many calls with just one subscribe
and catch
?
It triggers the call and handles the result. Coming back to the main topic of the article, if you are writing a lot of logic inside the subscribe callback, you are using an Observable as if it was a Promise. Thus, you are not making an optimal use of RxJS or Angular features.
Try something like:
import { map, switchMap } from 'rxjs/operators'
this.addressService.getById(this.data.id, this.data.userId).pipe(
switchMap(address => this.dropdownService.getCidadesBrByEstado(address.name).pipe(
// this pass both cities and address to the next observable in this chain
map(cities => ({ cities, address }))
))
).subscribe(({ cities, address }) => {
this.cities = cities
this.address = address
})
For angular when using RxJS, it suggests to use the Observable Class. To solve callback hell in the RxJS, You can use Observable' Operators api like switchMap() method(more methods for different scene are map(), concatMap(), ...). Here is my example about using the switchMap() method:
(1) Situation I met: I want to subsribe serviceC, but serviceC needs subsribe serviceB, and serviceB needs subsribe serviceA
const serviceA(params): Observable<any>;
const serviceB(params): Observable<any>;
const serviceC(params): Observable<any>;
serviceA(paramsA).subscribe(
serviceAResult => {
serviceB(paramsB).subscribe(
serviceBResult => {
serviceC(params).subscribe(
serviceCResult => {
// here is my logic code. Oh, Shit subscribe hell!
}
)
}
)
}
)
(2) Use switchMap() method to optimize code structure
const serviceB$ = serviceA(paramsA).pipe(
switchMap(serviceAResult => {
return serviceB(paramsB);
})
);
const serviceC$ = serviceB$.pipe(
switchMap(serviceBResult => {
return serviceC(paramsC);
})
);
serviceC$.subscribe(
serviceCResult => {
// here is my logic code.
},
error =>{
// handle error
}
);
Good post about dealing with callback hell.
Assuming, you don't actually care about streams you could also convert the Observables to promises in this case and use async/await
:
async ngOnInit(): Promise<void> {
this.address = await this.addressService.getById(this.data.id, this.data.userId).toPromise();
this.cities = await this.dropdownService.getCidadesBrByEstado(this.address.name).toPromise();
}
And make sure you also catch the errors. try catch
for example.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With