angular - Comment éviter l'enfer des callbacks subscribe de RxJs ?


J'utilise Angular RxJs subscribe pour effectuer un appel HttpClient puis faire un autre appel en utilisant les valeurs du premier. Dans ce cas, il y a un appel pour obtenir un objet adresse, puis je fais un appel en utilisant cet objet. Comme ceci :

@Injectable()
export class AddressService {
  constructor(private http: HttpClient) { }

  getById(addressId: string, userId: string) {
    return this.http.get(BACKEND_URL + 'getAddressById/' + [addressId, userId]);
  }
}
  
export class AddressModalComponent implements OnInit {
  constructor(private alertService: AlertService, private addressService: AddressService,           @Inject(MAT_DIALOG_DATA) public data: any, private dropdownService: DropdownService)

  ngOnInit() {
    this.addressService.getById(this.data.id, this.data.userId)
        .subscribe(
          (address: Address) => {
            this.dropdownService.getCidadesBrByEstado(address.name)
              .subscribe((cities: BrCity[]) => {
                this.cities = cities;
                this.address = address;
              },
              error => console.log(error));
          }, error => { this.alertService.error(error);
          }
        );
    }
  }
}

J'essaie d'éviter plusieurs abonnements (Subscribe), il y en a beaucoup comme ça dans mon code. J'ai besoin d'une approche Async/Await comme les promises de Node.js, mais en utilisant des Observables au niveau du composant. Je ne suis pas très familier avec les commandes RxJs... existe-t-il une meilleure façon de faire plusieurs appels avec un seul subscribe et un catch ?

Réponse #1

Essayez quelque chose comme :

import { map, switchMap } from 'rxjs/operators'

this.addressService.getById(this.data.id, this.data.userId).pipe(
  switchMap(address => this.dropdownService.getCidadesBrByEstado(address.name).pipe(
    // this pass both cities and address to the next observable in this chain
    map(cities => ({ cities, address }))
  ))
).subscribe(({ cities, address }) => {
  this.cities = cities
  this.address = address
})

Réponse #2

Pour Angular, lorsqu'on utilise RxJS, il est suggéré d'utiliser la classe Observable. Pour résoudre le problème du callback hell dans RxJS, vous pouvez utiliser l'API des opérateurs d'Observable comme la méthode switchMap() (d'autres méthodes pour différentes situations sont map(), concatMap(), ...). Voici mon exemple sur l'utilisation de la méthode switchMap() :
(1) Situation que j'ai rencontrée : je veux m'abonner à serviceC, mais serviceC a besoin de s'abonner à serviceB, et serviceB a besoin de s'abonner à serviceA

const serviceA(params): Observable<any>;
const serviceB(params): Observable<any>;
const serviceC(params): Observable<any>;

serviceA(paramsA).subscribe(
    serviceAResult => {
        serviceB(paramsB).subscribe(
            serviceBResult => {
                serviceC(params).subscribe(
                    serviceCResult => {
                        // here is my logic code. Oh, Shit subscribe hell!
                    }
                )
            }
        )
    }
)

(2) Utilisez la méthode switchMap() pour optimiser la structure du code

const serviceB$ = serviceA(paramsA).pipe(
    switchMap(serviceAResult => {
        return serviceB(paramsB);
    })
);

const serviceC$ = serviceB$.pipe(
    switchMap(serviceBResult => {
        return serviceC(paramsC);
    })
);

serviceC$.subscribe(
    serviceCResult => {
        // here is my logic code.
    },
    error =>{
        // handle error
    }
);

Bon article sur la gestion de l'enfer des rappels.