Chaining Observables in sequence, as you want to do in your code
Concerning your code example, if you want to chain Observables (trigger another after the previous emits), use flatMap
(or switchMap
) for this purpose :
this.serviceA.get()
.flatMap((res1: any) => this.serviceB.get())
.flatMap((res2: any) => this.serviceC.get())
.subscribe( (res3: any) => {
....
});
This one is better practice compared to nesting, as this will make things clearer and help you avoid callback hell, that Observable and Promises were supposed to help preventing in the first place.
Also, consider using switchMap
instead of flatMap
, basically it will allow to 'cancel' the other requests if the first one emits a new value. Nice to use if the first Observable that triggers the rest is some click event on a button, for instance.
If you don't need your various requests to wait in turn for each other, you can use forkJoin
or zip
to start them all at once, see @Dan Macak answer's for details and other insights.
Angular 'async' pipe and Observables work well together
Concerning Observables and Angular, you can perfectly use | async
pipe in a Angular template instead of subscribing to the Observable in your component code, to get the value(s) emitted by this Observable
ES6 async / await and Promises instead of Observables ?
if you're not feeling using Observable directly, you can simply use .toPromise()
on your Observable, and then some async/await instructions.
If your Observable is supposed to return only one result (as it is the case with basic API calls) , an Observable can be seen as quite equivalent to a Promise.
However, I'm not sure there is any need to do that, considering all the stuff that Observable already provide (to readers : enlightening counter-examples are welcome!) . I would be more in favor of using Observables whenever you can, as a training exercise.
Some interesting blog article on that (and there are plenty of others):
https://medium.com/@benlesh/rxjs-observable-interop-with-promises-and-async-await-bebb05306875
The toPromise function is actually a bit tricky, as it’s not
really an “operator”, rather it’s an RxJS-specific means of
subscribing to an Observable and wrap it in a promise. The promise
will resolve to the last emitted value of the Observable once the
Observable completes. That means that if the Observable emits the
value “hi” then waits 10 seconds before it completes, the returned
promise will wait 10 seconds before resolving “hi”. If the Observable
never completes, then the Promise never resolves.
NOTE: using toPromise() is an antipattern except in cases where you’re
dealing with an API that expects a Promise, such as async-await
(emphasis mine)
The example you requested
BTW, it will be nice if anyone can give me an example code to solve
this with async/await :D
Example if you really want to do it (probably with some mistakes, can't check right now, please feel free to correct)
// Warning, probable anti-pattern below
async myFunction() {
const res1 = await this.serviceA.get().toPromise();
const res2 = await this.serviceB.get().toPromise();
const res3 = await this.serviceC.get().toPromise();
// other stuff with results
}
In the case you can start all requests simultaneously, await Promise.all()
which should be more efficient, because none of the calls depends on the result of each other. (as would forkJoin
do with Observables)
async myFunction() {
const promise1 = this.serviceA.get().toPromise();
const promise2 = this.serviceB.get().toPromise();
const promise3 = this.serviceC.get().toPromise();
let res = await Promise.all([promise1, promise2, promise3]);
// here you can retrieve promises results,
// in res[0], res[1], res[2] respectively.
}
this.serviceA.get().flatMap((res1: any) => this.serviceB.get()).flatMap((res2: any) => this.serviceC.get()).subscribe( (res3: any) => { .... });
– Nadean