Объединение наблюдаемых последовательно и параллельно для извлечения данных из нескольких API - PullRequest
0 голосов
/ 02 ноября 2019

Я пытаюсь проверить достоверность функции, которую я написал в Typescript, в соответствии с наблюдаемыми RxJS, которая выбирает некоторые заказы из одного сервиса, а затем для каждого бронирования выбирает свое соответствующее местоположение и активность из другого сервиса.

Я просто пишу этот пост, чтобы проверить правильность написанного и спросить, могу ли я что-то сделать более эффективно.

let params = new HttpParams();
params = params.append('status', 'C');
params = params.append('offset', offset.toString());
params = params.append('limit', limit.toString());
return this.http.get(`${this.environment.booking.url}/my/bookings`, { params }).pipe(
    mergeMap((bookings: Booking[]) => {
        if(bookings.length > 0) {
            return forkJoin(
                bookings.map((booking: Booking) =>
                    forkJoin(
                        of(booking),
                        this.activityService.getActivity(booking.activity),
                  this.locationService.getLocation(booking.finalLocation),
                    ).pipe(
                        map((data: [ Booking, Activity, Location ]) => {
                            let booking = data[0];
                            booking.activityData = data[1];
                            booking.finalLocationData = data[2];
                            return booking;
                        })
                    )
                )
            )
        }

        return of([]);
    }),
    catchError((err: HttpErrorResponse) => throwError(err))
);

Я ожидаю эту функциювернуть список заказов вместе с их соответствующим местоположением и деятельностью. Однако, что более важно, я хочу убедиться, что то, что я делаю, правильно и разумно. Могу ли я что-нибудь сделать по-другому, чтобы сделать его чище / более читабельным (не придирчивым, пожалуйста ?)?

С другой стороны, по поводу производительности, у меня также есть дополнительный вопросЧто касается производительности. Учитывая, что список заказов имеет общие виды деятельности и места. Есть ли способ выбрать действия и местоположения без дублирования HTTP-запросов? Это уже обработано под капотом RxJS? Могу ли я что-нибудь сделать, чтобы сделать эту функцию более эффективной?

Ответы [ 2 ]

0 голосов
/ 02 ноября 2019

Я не уверен насчет эффективности, но, по крайней мере, мне было немного трудно читать

Вот как я бы это сделал:

Iиспользовал фиктивный API, но я думаю, что он коррелирует с вашей ситуацией

const usersUrl = 'https://jsonplaceholder.typicode.com/users';
const todosUrl = 'https://jsonplaceholder.typicode.com/todos';
const userIds$ = of([1, 2, 3]); // Bookings' equivalent

userIds$
  .pipe(
    filter(ids => ids.length !== 0),
    // Flatten the array so we can avoid another nesting level
    mergeMap(ids => from(ids)),
    // `concatMap` - the order matters!
    concatMap(
      id => forkJoin(ajax(`${usersUrl}/${id}`), ajax(`${todosUrl}/${id}`))
        .pipe(
          map(([user, todo]) => ({ id, user: user.response, todo: todo.response }))
        )
    ),
   toArray()
  )
  .subscribe(console.log)

Вот демонстрация StackBlitz.

СИмея это в виду, вот как я бы приспособил это к вашей проблеме:

this.http.get(`${this.environment.booking.url}/my/bookings`, { params }).pipe(
    filter(bookings => bookings.length !== 0),
    // Get each booking individually
    mergeMap(bookings => from(bookings)),
    concatMap(
        b => forkJoin(
            this.activityService.getActivity(b.activity),
            this.locationService.getLocation(b.finalLocation),
        )
        .pipe(
            map(([activity, location]) => ({ ...b, activity, location }))
        )
    ),
    // Getting the bookings array again
    toArray()
    catchError((err: HttpErrorResponse) => throwError(err))
);
0 голосов
/ 02 ноября 2019

Вот как я бы решил эту проблему, используя RxJS:

  1. Получить все Bookings
  2. Для каждой выборки бронирования Location и Activities cuncurrently

const { from, of, forkJoin, identity } = rxjs;
const { mergeMap, tap, catchError } = rxjs.operators;

const api = 'https://jsonplaceholder.typicode.com';

const endpoints = {
  bookings: () => `${api}/posts`,
  locations: (id) => `${api}/posts/${id}/comments`,
  activities: (id) => `${api}/users/${id}`
};

const fetch$ = link => from(fetch(link)).pipe(
  mergeMap(res => res.json()),
  catchError(() => from([])),
);

fetch$(endpoints.bookings()).pipe(
  mergeMap(identity),
  mergeMap(booking => forkJoin({
    booking: of(booking),
    locations: fetch$(endpoints.locations(booking.id)),
    activities: fetch$(endpoints.activities(booking.userId)),
  })),
).subscribe(console.log);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.js" integrity="sha256-Nihli32xEO2dsnrW29M+krVxoeDblkRBTkk5ZLQJ6O8=" crossorigin="anonymous"></script>

note :

  1. Реактивное программирование и более общие декларативные подходы с акцентом на избежаниеимперативные потоки управления ... Вы должны попытаться написать свои трубы без условий (или любого другого потока управления). Чтобы удалить пустые бронирования, вы можете использовать оператор filter.
  2. Избегать вложения потоков, поскольку это происходит за счет читабельности.
  3. forkJoin также принимает объект spec, который очень полезен (часть перегрузок)
...