Home > Blockchain >  RxJS how to fire two http requests in parallel but wait for first to complete before second value is
RxJS how to fire two http requests in parallel but wait for first to complete before second value is

Time:12-07

I have a UI where I display details about a Game. The view has two tabs, one with the details and one with the screenshots.

enter image description here

To construct the Model for the View, I need to get data from 2 different endpoints:

  • /games/:id
  • /games/:id/screenshots

The behaviour I am trying to achieve is as follow:

  • I want to parallelize the network requests to both API endpoint
  • I want the first request (/games/:id) always to emit first
  • I want the second request (/games/:id/screenshots) to emit last, even when it finished before the first one.

I have been able to achieve a working solution, but what I cannot achieve is "parallelize and wait".

This is my current implementation

export class GameService {
  constructor(private http: HttpClient) {}

  private state: GameState = {
    ...initialState,
  };
  private store = new BehaviorSubject<GameState>(this.state);
  private store$ = this.store.asObservable();

findGame(id: string) {
    const game$ = this.http.get<Game>(`${env.BASE_URL}/games/${id}`).pipe(
      delay(1500),
      tap((game) => {
        this.state = { ...this.state, selectedGame: game };
        this.store.next({ ...this.state });
      })
    );

    const screenshots$ = this.http
      .get<APIResponse<{ image: string }>>(
        `${env.BASE_URL}/games/${id}/screenshots`
      )
      .pipe(
        delay(2500),
        map(({ results: screenshots }) => screenshots),
        tap((screenshots) => {
          if (this.state.selectedGame) {
            this.state.selectedGame.screenshots = [...screenshots];
            this.store.next({ ...this.state });
          }
        })
      );

     return game$.pipe(concatMap(() => screenshots$));
  }
}

This line

return game$.pipe(concatMap(() => screenshots$));

allows me to wait for the first request to finish before the second one can emit, but it also means that the network request is not initiated until the first observable emits.

I have tried to use merge but it becomes very complex to determine the shape of the emitted value and the code becomes quite "dirty"

How can I parallelize both requests, but wait for the first to finish before the second one emits?

CodePudding user response:

You can use delayWhen.

import { of } from 'rxjs';
import { delay, delayWhen, shareReplay, tap } from 'rxjs/operators';

function getGame() {
  console.log('game requested');
  return of('foo').pipe(
     delay(2500),
     tap(() => console.log('game returned')),
  );
}

function getScreenshots() {
  console.log('screenshots requested');
  return of('bar').pipe(
     delay(1000),
     tap(() => console.log('screenshots returned')),
  );
}


const game$ = getGame().pipe(shareReplay(1));
const screenshots$ = getScreenshots().pipe(
  delayWhen(() => game$),
);

game$.subscribe(() => console.log('game received'));
screenshots$.subscribe(() => console.log('screenshots received'));

Stackblitz: https://stackblitz.com/edit/rxjs-6jmk4j?file=index.ts

  • Related