I am using angular 2 common http that return an Observable, but I face with a problem that my code likes a mesh when I use nested Observable call:
this.serviceA.get().subscribe((res1: any) => {
this.serviceB.get(res1).subscribe((res2: any) => {
this.serviceC.get(res2).subscribe((res3: any) => {
})
})
})
Now I wanna use async/await to avoid that but async/await only work with Promise. I know that Observable can be converted to Promise but as I know, it is not a good practice. So what should I do here ?
BTW, it will be nice if anyone can give me an example code to solve this with async/await :D
As @Pac0 already elaborated on the various solutions well, I will just add slightly different angle.
Mixing Promises and Observables
I personally prefer not mixing Promises and Observables - which is what you get while using async await with Observables, because even though they look similar, they are very different.
Use of Promises in Angular
Now even though it is sometimes valid to use both, especially with Angular I think one should consider going as far with RxJS as possible. The reasons being:
async
pipe which allows for composing your whole application data flow of streams which you filter, combine and do whatever modification you want on it without interrupting the stream of data coming from server without a single need for thening or subscribing. This way, you don't need to unwrap the data or assign it to some auxiliary variables, the data just flows from services through Observables straight to the template, which is just beautiful.There are some cases though where Promise still can shine. For example what I am missing in rxjs TypeScript types is concept of single. If you are creating an API to be used by others, returning Observable is not all that telling: Will you receive 1 value, many, or will it just complete? You have to write comment to explain it. On the other hand, Promise has much clearer contract in this case. It will always resolve with 1 value or reject with error (unless it hangs forever of course).
Generally, you definitely don't need to have only Promises or only Observables in your project. If you just want to express with a value that something was completed (deleting user, updating user), and you want to react on it without integrating it to some stream, Promise is the more natural way of doing so. Also, using
async/await
gives you the power to write code in sequential manner and therefore simplifying it greatly, so unless you need advanced management of incoming values, you can stay with Promise.Back to your example
So my recomendation is to embrace both the power of RxJS and Angular. Coming back to your example, you can write the code as following (credits for the idea to @Vayrex):
This piece of code will fire 3 requests and once all of those request Observables have completed, subscription callback to
forkJoin
will get you the results in an array, and as said, you can subscribe to it manually (as in the example) or do this declaratively usingresult$
andasync
pipe in the template.Using
Observable.zip
would get you the same result here, the difference betweenforkJoin
andzip
is that the former emits only last values of inner Observables, the latter combines first values of the inner Observables, then second values etc.Edit: Since you need the results of previous HTTP requests, use
flatMap
approach in @Pac0's answer.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
(orswitchMap
) for this purpose :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 offlatMap
, 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
orzip
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 ObservableES6 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
(emphasis mine)
The example you requested
Example if you really want to do it (probably with some mistakes, can't check right now, please feel free to correct)
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 wouldforkJoin
do with Observables)