开发者

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

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.

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

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 t开发者_高级运维o 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?


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜