Subscribe to multiple async http calls

2020-02-11 09:44发布

问题:

on ngOnInit i make 4 http request for data, after that i need to load the data from server to fill the forms with the data based on data model of last 4 http call.

In short words i need to subscribe to this 4 http calls, and make sure don't fail if they don't fail finally i can call the 5th http call to get data from server.

From what i understand, i should avoid to inest a observable and go with switch, but how to do this with 4 http call? should i create an observable wait for the http calls and if succed to use switchmap on the 5th http call?

This is the code.

      ngOnInit() {
    this.service.getProvince().subscribe(
        (value) => { return value; },
        (error: AppError) => {
            if (error instanceof NotFoundError) {
                console.log('Error richiesta http');
            } else {
                console.log(error);
            }
        });


    this.service.getLingueStraniere().subscribe(
        (value) => { return value; },
        (error: AppError) => {
            if (error instanceof NotFoundError) {
                console.log('Error richiesta http');
            } else {
                console.log(error);
            }
        });

    this.service.getTitoliPreferenziali().subscribe(
        (value) => { return value; },
        (error: AppError) => {
            if (error instanceof NotFoundError) {
                console.log('Error richiesta http');
            } else {
                console.log(error);
            }
        });

    this.service.getRiserve().subscribe(
        (value) => { return value; },
        (error: AppError) => {
            if (error instanceof NotFoundError) {
                console.log('Error richiesta http');
            } else {
                console.log(error);
            }
        });
}


// this is the 5th call that need to be done only if last 4 call not fail

finalCall {
    this.service.getDomanda().subscribe((domanda: any) => {
        this.popolaForm(domanda); // Method that use data to fill foms
    },
        (error: AppError) => {
            if (error instanceof NotFoundError) {
                console.log('Error richiesta http');
            } else {
                console.log(error);
            }
        });
} 

回答1:

You can use forkJoin to combine all the requests and get the results as callback when all the requests are completed.

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

ngOnInit() {
 forkJoin([this.service.getProvince(),this.service.getLingueStraniere(),this.service.getTitoliPreferenziali(), this.service.getRiserve()])
     .pipe(switchMap(result => { 
          console.log('Province', result[0]);
          console.log('LingueStraniere', result[1]);
          console.log('TitoliPreferenziali', result[2]);
          console.log('Riserve', result[3]);
          return this.service.getDomanda();
      }))
      .subscribe((domanda: any) => {
         console.log('getDomanda', domanda);
       });                                                          
}


回答2:

You can make use of forkJoin. It waits for all observables to be completed, before emitting the last emitted value from each request. forkJoin throws an error if any of the observables throws an error.

let callOne = this.service.getLingueStraniere();
let callTwo = this.service.getTitoliPreferenziali();
 .
 .
 .
forkJoin([callOne, callTwo]).subscribe(response => {
  //handle success response
}, (error) => {
    // error handling
}, () => {
    // when observable is completed
});

You may read more about forkJoin over here.



回答3:

I would transform those subscriptions to promises using .toPromise() so you can wait for all 4 http requests to be completed and then fill the form. It is not a fully running example but the idea is said.

try {
    ... await this.service.getProvince().toPromise();
    ... await this.service.getTitoliPreferenziali().toPromise();
    ... await this.service.getLingueStraniere().toPromise();
    ... await this.service.getRiserve().toPromise();
    //fill form
}catch(error) {
 //show error message or do something like retry calls
}