Отмена HTTP-запроса, когда кто-то другой вызывает тот же метод

#angular #rxjs

#angular #rxjs

Вопрос:

Я использую angular, ngrx и rxjs в своем приложении. Когда эффект вызывается дважды, первый http-запрос эффекта отменяется, и учитывается только второй.

Я хочу эмулировать это в функции, которая находится за пределами области действия ngrx.

У меня есть следующий метод :

 checkMissionPoint(
    positions: Array<Cesium.Cartographic>
  ): Observable<{
    valid: boolean;
    response: Array<CheckWaypointResponse>;
  }> {

    return this.store$.pipe(
      select(TransactionsStoreSelectors.selectedTransactionId),
      take(1),
      switchMap((tId) =>
        this.httpClient
          .post<Array<CheckWaypointResponse>>(
            HTTPUtils.getPath(`${environment.FLIGHT_URL}/trajectories/${tId}/checkpoints`),
            {positions}
          )
          .pipe(
            switchMap((result) => {
              return resu<
            })
          )
      )
    );
  }
 

Я вызываю этот метод следующим образом в своем приложении в разных местах приложения

     this.missionHelperService.checkMissionPoint(positions)
 

Теперь я знаю какой-то уродливый способ отменить его, сохранив как ссылку и отписавшись, если метод вызывается снова, а ссылка все еще существует. Но мне интересно, есть ли хороший способ rxjs для достижения этой цели.

Спасибо.

РЕДАКТИРОВАТЬ: я сделал небольшой stackblitz, но вам нужно настроить МЕДЛЕННОЕ 3G-соединение в вашей консоли Chrome https://angular-xm2ixt.stackblitz.io

вы заметите, что obs вызывается дважды, api будет как запускаться, так и отвечать, я хочу, чтобы второй вызов отменял первый (и отправлял сообщение об ошибке тому, кто слушал, и sub 2 будет работать нормально)

Комментарии:

1. Я немного смущен тем, что вы спрашиваете. Вы хотите сказать, что опубликованный вами код работает? Вы просто хотите ту же функциональность без rxjs?

2. Не могли бы вы предоставить демонстрационную версию stackblitz? Это было бы полезно.

Ответ №1:

У меня была очень похожая ситуация, вот как я ее решил.

 checkMissionPointCalled = new EventEmitter<void>();

checkMissionPoint(
    positions: Array<Cesium.Cartographic>
): Observable<{
    valid: boolean;
    response: Array<CheckWaypointResponse>;
}> {
    this.checkMissionPointCalled.emit();
    return this.store$.pipe(
        select(TransactionsStoreSelectors.selectedTransactionId),
        take(1),
        switchMap((tId) =>
            this.httpClient
                .post<Array<CheckWaypointResponse>>(
                     HTTPUtils.getPath(`${environment.FLIGHT_URL}/trajectories/${tId}/checkpoints`),
                        { positions }
                )
         ),
        takeUntil(this.checkMissionPointCalled)
    );
}
 

Однако я не смог использовать

          .pipe(
            switchMap((result) => {
              return resu<
            })
          )
 

после httpClient.post вызова, я думаю, вероятно, это была ошибка. Итак, я не включил его в свой ответ.

Вероятно, вы также захотите закрыть checkMissionPointCalled ngOnDestroy метод.

Комментарии:

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

2. ваше решение действительно работает, я думаю, что это не лучший rxjs, но оно делает свое дело, спасибо. и поскольку здесь больше никто не отвечает…

Ответ №2:

Проблема, с которой вы столкнулись сейчас, заключается в том, что checkMissionPoint() метод возвращает совершенно новый наблюдаемый объект для каждого вызывающего.

Я думаю, вы могли бы добиться того, что пытаетесь сделать, если бы у вас был частный наблюдаемый объект и возвращал тот же наблюдаемый объект всем вызывающим. Это наблюдаемое значение будет использоваться switchMap() для достижения требуемого поведения отмены.

 private positions$ = new Subject();
private uri$ = this.store$.pipe(
    select(TransactionsStoreSelectors.selectedTransactionId),
    map(tId => HTTPUtils.getPath(`${environment.FLIGHT_URL}/trajectories/${tId}/checkpoints`)
);
private checkMissionPoint$ = combineLatest([this.uri$, this.positions$]).pipe(
    switchMap(([uri, positions]) => this.httpClient.post<CheckWaypointResponse[]>(uri, {positions}))
);

checkMissionPoint(
    positions: Array<Cesium.Cartographic>
  ): Observable<{
    valid: boolean;
    response: Array<CheckWaypointResponse>;
  }> {
    this.positions.next(positions);

    return this.checkMissionPoint$.pipe(
      take(1)
    );
}
 

В зависимости от вашего варианта использования, может быть более чистым вернуть метод void и сделать общедоступным общий наблюдаемый:

 private positions$ = new Subject();
private uri$ = this.store$.pipe(
    select(TransactionsStoreSelectors.selectedTransactionId),
    map(tId => HTTPUtils.getPath(`${environment.FLIGHT_URL}/trajectories/${tId}/checkpoints`)
);
public missionPoint$ = combineLatest([this.uri$, this.positions$]).pipe(
    switchMap(([uri, positions]) => this.httpClient.post<CheckWaypointResponse[]>(uri, {positions}))
);

setPosition(
    positions: Array<Cesium.Cartographic>
  ): Observable<{
    valid: boolean;
    response: Array<CheckWaypointResponse>;
  }> {
    this.positions.next(positions);
}
 

Комментарии:

1. ваш ответ содержит ошибки и опечатки, но он решает проблему. Спасибо.

2. Я попробовал первое решение, потому что оно лучше подходит для моего использования, оно отменяет, но создает больше вызовов api с последними параметрами. например, я вызываю 1 раз, отменяю, следующий вызов создаст 2 вызова api с теми же параметрами, затем отмените, 3 сделают 3 и т.д… Я думаю, что вызов cancel должен возвращать ошибку или что-то в этом роде.

3. switchMap отменит текущие вызовы API в процессе выполнения при поступлении нового. exhaustMap будет игнорировать новые запросы, пока не будет выполнен текущий.

4. @MrkSef switchMap — это то, что я хочу, и что в ответе, проблема, он отменяет API, но вызывает несколько вместо одного. Но если в ответе нет проблем, то это может быть в моем коде?