Являются ли наблюдаемые правильным решением для запросов, которые зависят от данных друг от друга? - PullRequest
1 голос
/ 10 марта 2020

У меня есть два API, с которыми мне нужно интегрироваться. Один я контролирую, другой нет.

В настоящее время у меня есть веб-приложение, которое должно получать пользовательские данные из обоих API и объединять их. Это происходит один раз, когда пользователь входит в систему.

Веб-приложение построено с Angular и поэтому имеет встроенные Observables, но я не очень хорош в наблюдаемых, они чувствуют себя очень сложными в том, что должно быть простой пример использования.

У меня есть код ниже, который делает запрос к поставщику удостоверений, получает значение от этого поставщика и запрашивает мой API для пользователя с тем же значением, затем он загружает данные для этого пользователя, и связанный объект, чтобы его можно было отобразить в веб-приложении.

По сути, мне нужно отобразить объединенное представление пользователя и его материнской компании. Я могу получить доступ к компании только по гиперссылке от пользователя и не могу узнать пользователя, не посмотрев его сначала в провайдере идентификации. Цепочка данных, которые мне нужны для выполнения всех этих запросов, - это подробности аутентификации => данные пользователя => компания - я просматриваю детали аутентификации, использую значение из этого, чтобы найти пользователя, и, получив пользователя, затем я смотрю вверх по компании пользователя.

Функция, которую я написал, кажется неуклюжей и наивной. Есть ли лучший способ сделать это с Rx JS?

getUserData() {
        this.authService.getUser$()
            .pipe(take(1))
            .subscribe(
                profile => {
                    this.authProfile = profile;
                    this.getUserByAuthId(encodeURI(profile.sub))
                        .pipe(take(1))
                        .subscribe(user => {

                            this.userProfile = user;

                            if (user._links.employer) {
                                this.http.get<Company>(user._links.employer.href)
                                    .pipe(take(1))
                                    .subscribe(
                                        company => {
                                            this.company = company;
                                        }
                                    );
                            }
                });
            });
    }

Ответы [ 3 ]

2 голосов
/ 10 марта 2020

Попробуйте использовать concatMap как , он не подписывается на следующую наблюдаемую, пока предыдущая не завершит :

return this.authService.getUser$()
    .pipe(
        concatMap((res: a1) => {           
            this.getUserByAuthId(encodeURI(res.sub))
        }),
        concatMap((res: a2) => {           
            this.http.get<Company>(res._links.employer.href)
        })
    ).subscribe((res: a3) => {
        // your logic here
});
1 голос
/ 10 марта 2020

Да, вложенные subscribes не хороши, потому что они вызывают ад обратного вызова и не могут быть отменены.

Вам необходимо использовать switchMap в подобных ситуациях. SwitchMap позволяет вам условно переключаться на новую наблюдаемую.

Попробуйте:

import { of } from 'rxjs';
import { switchMap } from 'rxjs/operators';
......
getUserData() {
  this.authService.getUser$().pipe(
    take(1),
    switchMap(profile => {
      this.authProfile = profile;
      return this.getUserByAuthId(encodeURI(profile.sub)),
    }),
    take(1),
    switchMap(user => {
      this.userProfile = user;
      if (user._links.employer) {
        return this.http.get<Company>(user._links.employer.href);
      } else {
        return of(undefined);
      }
    }),
  ).subscribe(company => {
    // assign either undefined to company or the value of the previous HTTP call
    this.company = company;
  });
}
0 голосов
/ 10 марта 2020

Для удобства чтения я предпочитаю asyn c, ожидаю:

getUserData() {
    this.authProfile = await this.authService.getUser$()
            .pipe(take(1)).toPromise();
    this.userProfile = await this.getUserByAuthId(encodeURI(this.authProfile.sub))
            .pipe(take(1)).toPromise();
    if (this.userProfile._links.employer) {
        this.company = this.http.get<Company>(this.userProfile._links.employer.href)
                .pipe(take(1)).toPromise();
    }
} 
...