Búsqueda de sitios web

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:

  1. 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.
  2. intenta evitar los subscribe anidados: no solo ignoras el poderoso potencial de los Observable, 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 el Observable podría ayudarte a "compartir" los resultados de la solicitud o transformarlos de alguna manera.
  3. flatMap/mergeMap, concatMap y switchMap funcionan de manera diferente, proporcionándote la capacidad de controlar el comportamiento como desees. Sin embargo, para http.get() funcionan de manera bastante similar, es una buena idea comenzar a aprender esos operadores de combinación lo antes posible.
  4. 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 .subscribe te 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);

Artículos relacionados