Reputation: 1042
Up until now, I've been creating functions that return directly from HTTP get, and then calling .subscribe
on the return value. However, recently I've learned about Promise.all
, and would like to know how I can use it to wait for both HTTP gets to be completed.
Example of how I'm currently doing it:
function getCustomerList() {
return this.http.get("http://myapi.com/getCustomerList");
}
function otherFunction() {
this.getCustomerList().subscribe() {
data => {}, err => {}, () => {}
}
}
This seems to work OK, but I keep wondering if I could do something like this instead:
function getCustomerList() {
return this.http.get("http://myapi.com/getCustomerList").subscribe( data => {}, err => {}, () => {});
}
function getVendorList() {
return this.http.get("http://myapi.com/getVendorList").subscribe( data => {}, err => {}, () => {});
}
function getAllInfo() {
var p1 = getCustomerList();
var p2 = getVendorList();
Promise.All([p1, p2]);
console.log("This should not run until both have returned");
}
But it always runs right away. So I tried to use .toPromise()
in the get()
functions with .then()
, and it did the same thing. The only thing I haven't tried is maybe putting a .toPromise()
on the outer function.
Upvotes: 3
Views: 3991
Reputation: 25525
The reason they ran right away is because you returned the subscription. You could do this:
function getCustomerList() {
return this.http.get("http://myapi.com/getCustomerList").toPromise();
}
function getVendorList() {
return this.http.get("http://myapi.com/getVendorList").toPromise();
}
function getAllInfo() {
const p1 = getCustomerList();
const p2 = getVendorList();
Promise.all([p1, p2]).then(values => {
console.log("This should not run until both have returned", values);
});
}
But even better, you could use forkJoin:
import { forkJoin } from 'rxjs';
// component declaration and constructor omitted
customerList$ = this.http.get("http://myapi.com/getCustomerList");
vendorList$ = this.http.get("http://myapi.com/getVendorList");
getAll() {
forkJoin(this.customerList$, this.vendorList$).subscribe(result => {
console.log(result);
// [{ customerList: ... }, {vendorList: ... }];
});
}
Upvotes: 5
Reputation: 68655
You are using not Promises
in the Promise.all
, but Observables
. You need to promisify
them using .toPromise()
. Then you need to continue your logic after fulfillment of the promise.
function getCustomerList() {
return this.http.get("http://myapi.com/getCustomerList").toPromise();
}
function getVendorList() {
return this.http.get("http://myapi.com/getVendorList").toPromise();
}
function getAllInfo() {
var p1 = getCustomerList();
var p2 = getVendorList();
Promise.All([p1, p2]).then(_ => console.log("This should not run until both have returned"));
}
As @jonrsharpe mentioned in the comments, it is not logically not wrap observable
into promise
for getting parallel work. You can use various of RxJS operators like combineLatest
or zip
to make wait for two observable to finish their work.
Upvotes: 2
Reputation: 38111
The first thing you have to realize is that observables are lazy, meaning they don't do anything (in this case, make the request) until you subscribe to them.
Promises, on the other hand, are eager, meaning they start doing work straight away, whether you do anything with the promise or not.
So yes, by using promises, you will make those requests straight away.
Here is how you would be best to structure your code to do what you want:
function getCustomerList(): Observable<CustomerList> {
return this.http.get("http://myapi.com/getCustomerList");
}
function getVendorList(): Observable<VendorList> {
return this.http.get("http://myapi.com/getVendorList");
}
async function getAllInfo() {
var p1 = getCustomerList();
var p2 = getVendorList();
await Promise.all([
p1.toPromise(),
p2.toPromise(),
]);
console.log("This should not run until both have returned");
}
By using async/await
and converting to promises just for the call to Promise.all()
, you will achieve exactly what you want to do.
Since the Http service returns observables, which are kind of like super-promises, you can just use an observable operator to combine the two.
The only downside is you won't be able to use async/await
with it.
Simply use combineLatest()
to combine the two return values as an array, just like with Promise.all().
function getCustomerList() {
return this.http.get("http://myapi.com/getCustomerList");
}
function getVendorList() {
return this.http.get("http://myapi.com/getVendorList");
}
function getAllInfo() {
var p1 = getCustomerList();
var p2 = getVendorList();
combineLatest(p1, p2)
.subscribe(([customerList, vendorList]) => {
console.log("This should not run until both have returned");
});
}
Or you could return the result of combineLatest()
, rather than subscribing to it directly.
Upvotes: 2
Reputation: 2290
when using .toPromise()
you no longer have to subscribe
return this.http.get("http://myapi.com/getCustomerList").toPromise();
also your log won't wait for the promises to complete. You'll either have to log in a .then()
function.
Promise.All([p1, p2])
.then(t => console.log("This should not run until both have returned"));
or mark getAllInfo
as async
and await
the Promise.all
await Promise.All([p1, p2]);
console.log("This should not run until both have returned");
I'd recommend looking more into async/await as well and not so much into observables. Just always convert them using .toPromise()
Upvotes: 2