Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait for multiple http requests to finish before running a function in angular

I have multiple http posts that fetches JSON data from a MySQL database. I want a function to listen to all of them and run the next piece of code once all of them returned that data has been received.

I've tried multiple variations of async with await, but the function doesn't appear to wait for the HTTP post to finish.

I've got a risk service risk.service.ts to get the riskTable for instance:

getRiskTable(): Observable<Risk[]> {
    return this.http.get(`${this.baseUrl}/getRiskTable`).pipe(
      map((res) => {
        this.riskTable = res['data'];
        return this.riskTable;
      }),
      catchError(this.handleError));
  }

On my Dashboard I call this code:

getRiskTable():any {
    return this.riskService.getRiskTable().subscribe(
      (res: []) => {
        this.riskTable = res; // the value is correctly captured here
        return true;
      },
      (err) => {
        this.error = err;
      }
    );
  }

And then an async function that runs multiple of these functions, in theory it's suppose to wait until all the functions complete and then log the value. But for some reason the global variable this.riskTable is undefined in this function.

async getAllData(){
    let riskTable = await this.getRiskTable();
    let risks = await this.getAllRisks();

    console.log(riskTable); //This returns stuff
    console.log(risks);
    console.log(this.riskTable); //This returns undefined even though it was set in getRiskTable
  }

Please any pointers - I am very new to angular and the complexity of this has me completely stumped. 2 Days and 100x variations to get this to work I am slowly losing my bananas!

like image 223
Brenden Engelbrecht Avatar asked Oct 15 '25 18:10

Brenden Engelbrecht


2 Answers

First off the async/await keywords are for use with promises, not Observables. So you can convert an observable to a promise using toPromise() or you can use rxjs ForkJoin. You also need to change the methods to return either an observable or return a promise, you can't do much with Subscription which is what subscribe returns when you call it and that is what you are passing now from getRiskTable.

One other issue is that you are not taking advantage of the type system, do not use any everywhere because these errors could have been caught at transpile time and now you have to guess why it's failing at run time which is more difficult.

import { forkJoin } from 'rxjs';
import { shareReplay } from 'rxjs/operators';

getRiskTable() : Observable<Risk[]> {
  const tableObservable = this.riskService.getRiskTable().pipe(shareReplay());
  tableObservable.subscribe((res) => this.riskTable = res, (err) => {this.error = err;});
  return tableObservable;
}

getAllData() {
  let riskTable = this.getRiskTable();
  let risks = this.getAllRisks(); // should also return some observable

  forkJoin(riskTable, risks).subscribe(_ => {
    // all observables have been completed
  });
}

Note that I also added shareReplay which ensures that multiple subscriptions to the same observable do not result in multiple calls to your API end point. One result is shared between multiple subscriptions to the observable.

like image 161
Igor Avatar answered Oct 17 '25 10:10

Igor


Since you are making use of Angular, you should use RxJS's forkJoin to combine the observables from both api request methods into a single value observable, and return the values by subscribing to it.

Do remember to import forkJoin into your component.

import { forkJoin } from 'rxjs';

..

getAllData(){
  const riskTable = this.riskService.getRiskTable();
  const risks = this.riskService.getAllRisk();

  forkJoin(riskTable, risks).subscribe(response => { 
    console.log(response);
  })
}

If you insist on handling it as a Promise instead of handling it as an Observable, you can utilise Promise.all() which returns them as a single Promise.

like image 33
wentjun Avatar answered Oct 17 '25 10:10

wentjun