Как выполнить HTTP-запрос в фоновом режиме с RxJS - PullRequest
0 голосов
/ 23 февраля 2020

У меня есть компонент мастера Angular, содержащий 2 шага. На первом этапе форма показывается пользователю. На втором шаге другая форма отображается в результате продолжительного HTTP GET. В настоящее время это работает, когда пользователь прибывает на второй шаг, выполняется запрос, и в конечном итоге пользователь видит форму как результат продолжительного вызова; однако пользователю досадно ждать окончания запроса.

Чтобы оптимизировать, я пытаюсь запустить длительный запрос на первом шаге мастера в фоновом режиме , чтобы сократить время ожидания для пользователя. Технически это возможно, потому что параметры запроса не зависят от действий пользователя на шаге 1.

Однако трудность состоит в том, что Observables ничего не делают, если вы не подписаны на них. Но когда я подписываюсь на наблюдаемый запрос, на шаге 1 мастера он будет ждать его завершения (таким образом, для пользователя не будет никакого повышения производительности).

Так что мне нужно как-то подписаться на наблюдаемый запрос, но в фоновом режиме, который не блокирует конвейер begin .

Каждый шаг мастера имеет onStepBegin и onStepFinish. На шаге 1 onStepBegin я хочу немедленно создать наблюдаемое и выполнить запрос. Затем на шаге 2 onStepBegin следует дождаться оставшегося времени запроса.

Технически код, приведенный ниже, работает, но запрос запускается не ранее, чем на шаге 2. Как я могу заставить его немедленно запустить запрос на шаге 1?

Я попробовал следующие вещи чтобы исправить это:

  • Добавьте оператор publi sh, чтобы сделать наблюдаемое горячим: не помогает, так как оно станет горячим после подписка
  • Подписаться с ручным subscribe() вызовом; но так называемые вложенные подписки создают утечки памяти

Мастер шаг 1:

public onStepBegin(begin: Observable<any>): Observable<MyContext> {
    return begin.pipe(
        tap(() => {
             ..
            this.context.loadInitial = this.loadInitialConfigurations(this.selectedElementclass.id).pipe(
                first(),
                publish()
            );
        })
    );
}

Мастер шаг 2:

public onStepBegin(begin: Observable<any>): Observable<MyContext> {
    return begin.pipe(
        switchMap(() => this.context.loadInitial),
        tap((c) => {
            this.context.configurations = c;
        }),
    );

1 Ответ

2 голосов
/ 23 февраля 2020

Опираясь на комментарий Джона к вашему вопросу, вам нужно запустить HTTP-запрос для данных шага 2 одновременно с началом шага 1.

Когда ответ HTTP возвращается для данных шага 2 , вы можете обновить тему, которую вы создали.

Когда приходит время перейти к шагу 2, вы теперь подписываетесь на эту тему.

Если ответ HTTP уже возвращен, значит, у вас уже есть данные. Если HTTP-запрос все еще обрабатывается, шаг 2 начнется, как только ответ вернется.

private step2data$: Subject<any> = new ReplaySubject<any>(1);

ngOnInit(): void {
  // kick off step one - however you do that
  this.beginStepOne().subscribe();

  // get step 2 data - however you do that
  this.http.get('http://mydatasource.com').subscribe(data => {
    this.step2data$.next(data);
  });
}

ngOnDestroy(): void {
  // tidy up
  this.step2data$.complete();
}

private beginStepOne(): Observable<MyContext> {
  // TODO: implement
}

// called when moving from step 1 to 2 - however you do that
private beginStepTwo(): Observable<MyContext> {
  // get data immediately if present, or wait for ongoing request to come back
  return this.step2data$.pipe(
    // we only need to take the first - like a normal http request
    take(1),
    // now switch to whatever method you use to kick off step 2
    switchMap(data => /* TODO: implement  */)
  );
}
...