Reputation: 51
This is my code, I start with an empty array and want to call a service that does some work with the populated array. I populate the array using two calls to services which call an API to receive elements to insert into the array.
I can't figure out how to have the last service call wait until the array has been populated before excuting. I think the fact that I have two seperate service calls to wait for, and the fact that they both have forEach loops makes this harder than it could have been. Any ideas?
const subscribers: ISubscriber[] = [];
this.selectedChildren.forEach(child => {
this.serviceA.getSubscribers(child.id).subscribe( (subs: ISubscriber[]) => {
subs.forEach(s => {
subscribers.push(s);
});
});
});
this.selectedSubscribers.forEach(sub => {
this.serviceB.getSubscriber(sub.subscriberId).subscribe( (sub: ISubscriber) => {
subscribers.push(sub);
});
});
// subscribers is always empty when this call is made
// since above code hasn't finished executing
this.serviceC.processSubscribers(subscribers).subscribe( sub => {
this.toastr.success('Success!');
});
async/await attempt that doesn't work:
async doSomething(){
const subscribers: ISubscriber[] = [];
await this.selectedChildren.forEach(async child => {
await this.serviceA.getSubscribers(child.id).subscribe( (subs: ISubscriber[]) => {
subs.forEach(s => {
subscribers.push(s);
});
});
});
await this.selectedSubscribers.forEach(async sub => {
await this.serviceB.getSubscriber(sub.subscriberId).subscribe( (sub: ISubscriber) => {
subscribers.push(sub);
});
});
// subscribers is always empty when this call is made
// since above code hasn't finished executing
this.serviceC.processSubscribers(this.id, subscribers).subscribe( id => {
this.toastr.success('Success!');
});
}
Promise.all attempt that doesn't work:
doSomething(){
const subscribers: ISubscriber[] = [];
const promises = [];
this.selectedChildren.forEach(child => {
promises.push(this.serviceA.getSubscribers(child.id).subscribe( (subs: ISubscriber[]) => {
subs.forEach(s => {
subscribers.push(s);
});
}));
});
this.selectedSubscribers.forEach(sub => {
promises.push(this.serviceB.getSubscriber(sub.subscriberId).subscribe( (sub: ISubscriber) => {
subscribers.push(sub);
}));
});
// subscribers is always empty when this call is made
// since above code hasn't finished executing
Promise.all(promises).then( a => {
this.serviceC.processSubscribers(this.id, subscribers).subscribe( id => {
this.toastr.success('Success!');
});
});
}
Upvotes: 2
Views: 2667
Reputation: 51
This is the solution I found that ended up working using promises:
doSomething(){
const subscribers: ISubscriber[] = [];
const promises = [];
this.selectedChildren.forEach(child => {
promises.push(this.serviceA.getSubscribers(child.id).toPromise().then( (subs: ISubscriber[]) => {
subs.forEach(s => {
subscribers.push(s);
});
}));
});
this.selectedSubscribers.forEach(sub => {
promises.push(this.serviceB.getSubscriber(sub.subscriberId).toPromise().then( (sub: ISubscriber) => {
subscribers.push(sub);
}));
});
// subscribers is always empty when this call is made
// since above code hasn't finished executing
Promise.all(promises).then( a => {
this.serviceC.processSubscribers(this.id, subscribers).subscribe( id => {
this.toastr.success('Success!');
});
});
}
Upvotes: 0
Reputation: 2403
Solution using rxjs operators :
const subscribers: ISubscriber[] = [];
const subscriberObservables = [];
// First you add all async calls into an array
this.selectedChildren.forEach(child => {
subscriberObservables.push(this.serviceA.getSubscribers(child.id));
});
this.selectedSubscribers.forEach(sub => {
subscriberObservables.push(this.serviceB.getSubscriber(sub.subscriberId));
});
// then you run all of them in parallel
combineLatest(subscriberObservables)
.pipe(
map((arrayOfArrays:ISubscriber[][]) => arrayOfArrays.flat()),
switchMap( (subs: ISubscriber[]) => this.serviceC.processSubscribers(subscribers))
)
.subscribe( sub => {
this.toastr.success('Success!');
});
Upvotes: 3