My Angular 2 application has 2 methods (GetCategories()
and GetCartItems()
) in a service , and both of these methods return Observable
s.
In order to invoke these two methods one after another from my component, I have written below code:
ngOnInit()
{
this.appService.GetCategories().subscribe( (data) => {
this.appService.categories = data;
this.appService.GetCartItems().subscribe( {
next: (data) => { this.appService.cart = data},
error: (err) => { this.toaster.error('cart==>' + err)}
})
});
}
Basically, calling GetCartItems()
from within subscribe()
of GetCategories()
, and I feel this is NOT the right approach. This is kind of callback hell.
Any idea on how to implement this in a better way (like chaining then()
in Promise
s)?
Looks like GetCartItems
doens't depend on GetCategories
. Then you can use zip:
Observable
.zip(
this.appService.GetCategories()
this.appService.GetCartItems()
)
.catch(err => this.toaster.error(err))
.subscribe(([categories, cartItems]) => {
this.appService.categories = categories;
this.appService.cart = cartItems;
});
This is most typically done with concat()
, concatMap()
or eventually concatAll()
depending on your usecase and whetrher you need to call both services in order or not.
function GetCategories() {
return Observable.timer(1000).do(() => console.log('GetCategories()'));
}
function GetCartItems() {
return Observable.timer(1000).do(() => console.log('GetCartItems()'));
}
console.log('start...');
GetCategories()
.concatMap(() => GetCartItems())
.subscribe(() => console.log('done'));
This prints to console:
start...
GetCategories()
GetCartItems()
done
Each item is delayed to show these are called in order one after another.
If you don't need to keep the same order you can use merge()
or mergeMap()
.
See live demo: https://jsbin.com/wawajob/1/edit
Note that using zip()
might have undesired behavior. See https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/zip.md