javascript - Mejor manera de realizar múltiples solicitudes HTTP en Angular
Estoy intentando enviar 2 solicitudes HTTP una por una; si la primera tiene éxito, enviar la segunda, si no, mostrar el mensaje de error correspondiente relacionado con la primera solicitud.
Estoy planeando usar algo así, pero no estoy seguro si es la mejor opción para este escenario:
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-root',
templateUrl: 'app/app.component.html'
})
export class AppComponent {
loadedCharacter: {};
constructor(private http: HttpClient) {}
ngOnInit() {
this.http.get('/api/people/1').subscribe(character => {
this.http.get(character.homeworld).subscribe(homeworld => {
character.homeworld = homeworld;
this.loadedCharacter = character;
});
});
}
}
Tengo diferentes solicitudes, por ejemplo, PUT y CREATE, también usando este enfoque. Sé que hay otras formas, por ejemplo, forkjoin, mergemap, pero si esta resuelve mi problema, parece ser más legible. ¿Alguna idea?
Respuesta #1
Primero que nada, tu código funciona y eso es genial: puedes dejarlo como está y todo estará bien.
Por otro lado, hay una manera de múltiples mejoras que te ayudarán a ti y a tus colegas en el futuro:
- intenta mover la lógica relacionada con http al servicio en lugar de llamar a http en los componentes; esto te ayudará a dividir el código en lógica relacionada con la vista y la lógica relacionada con el negocio/la obtención/la transformación.
- intenta evitar los
subscribeanidados: no solo ignoras el poderoso potencial de losObservable, sino que también atas el código a un flujo determinado sin la capacidad de reutilizar estas líneas en algún lugar de la aplicación. Devolver elObservablepodría ayudarte a "compartir" los resultados de la solicitud o transformarlos de alguna manera. flatMap/mergeMap,concatMapyswitchMapfuncionan de manera diferente, proporcionándote la capacidad de controlar el comportamiento como desees. Sin embargo, parahttp.get()funcionan de manera bastante similar, es una buena idea comenzar a aprender esos operadores de combinación lo antes posible.- piensa en cómo manejarás los errores en este caso: ¿qué pasará si tu primera llamada resulta en un error?
Observables tienen un mecanismo poderoso para tratarlos, mientras que.subscribete permite manejar un error solo de una manera.
Un ejemplo usando el switchMap:
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-root',
templateUrl: 'app/app.component.html'
})
export class AppComponent {
loadedCharacter: {};
constructor(private http: HttpClient) {}
ngOnInit() {
const character$ = this.http.get('/api/people/1').pipe(
tap(character => this.characterWithoutHomeworld = character), // setting some "in-between" variable
switchMap(character => {
return this.http.get(character.homeworld).pipe(
map(homeworld => {
return {
...character,
homeworld: homeworld
}
}
)
)
}),
catchError(errorForFirstOrSecondCall => {
console.error('An error occurred: ', errorForFirstOrSecondCall);
// if you want to handle this error and return some empty data use:
// return of({});
// otherwise:
throw new Error('Error: ' + errorForFirstOrSecondCall.message);
})
);
// you can either store this variable as `this.character$` or immediately subscribe to it like:
character$.subscribe(loadedCharacter => {
this.loadedCharacter = loadedCharacter;
}, errorForFirstOrSecondCall => {
console.error('An error occurred: ', errorForFirstOrSecondCall);
})
}
}
Respuesta #2
2 suscripciones anidadas nunca son el camino a seguir. Recomiendo este enfoque:
this.http.get('/api/people/1').pipe(
switchMap(character => this.http.get(character.homeworld).pipe(
map(homeworld => ({ ...character, homeworld })),
)),
).subscribe(character => this.loadedCharacter = character);
Editar: Para tu universidad
this.http.get('/api/people/1').pipe(
switchMap(character => this.http.get(character.university).pipe(
map(university => ({ ...character, university})),
)),
).subscribe(character => this.loadedCharacter = character);
O incluso encadenar solicitudes de universidad y planeta natal
this.http.get('/api/people/1').pipe(
switchMap(character => this.http.get(character.homeworld).pipe(
map(homeworld => ({ ...character, homeworld })),
// catchError(err => of({ ...character, homeworld: dummyHomeworld })),
)),
switchMap(character => this.http.get(character.university).pipe(
map(university => ({ ...character, university})),
)),
).subscribe(character => this.loadedCharacter = character);
