Как отменить http.post (), пока он ждет ответа?

Это некоторый код из углового компонента. Это компонент поиска, где characterIndexes - это массив результатов поиска.

Результаты поиска извлекаются путем ввода в поле поиска, которое запускает searchtrigger или searchEmptyTrigger зависимости от его содержимого. Получив первые результаты, я должен выполнить еще один http.post() чтобы получить имена, принадлежащие индексам, чтобы я мог их сортировать (я пропустил эту часть из кода). Затем результаты вставляются в characterIndex -array.

Аналогичная ситуация возникает при срабатывании searchEmptyTrigger . За исключением того, что characterIndex -array просто установлен в пустую, и никакие http-запросы не нужны.

Проблема, с которой я сталкиваюсь, заключается в том, что в некоторых случаях, когда searchEmptyTrigger , код из searchtrigger все еще работает (из-за задержек из-за http-запросов).

В результате characterIndexes сначала пустые. И затем он будет заполняться снова после получения результата из http-запроса в searchtrigger .

Поэтому большой вопрос: «Как отменить мой запуск http.post() пока он ждет ответа?»

  public characters: any[];
  public characterIndexes: number[];

  let searchBox = document.getElementById('search-box');
  let searchTrigger = fromEvent(searchBox, 'input')
  .pipe(
    map((event: any) => event.target.value ),
    filter( text => text.length > 2 ),
    debounceTime( 500 ),
    distinctUntilChanged(),
    switchMap( text =>  ajax(`https://esi.evetech.net/v2/search/?categories=character&datasource=tranquility&language=en-us&search=^${text}&strict=false`)
    )
  );

  let searchEmptyTrigger = fromEvent(searchBox, 'input')
  .pipe(
    map((event: any) => event.target.value ),
    filter( text => text.length <= 2 )
  );

  searchTrigger.subscribe( response => {
    if( response.response.character ){
      let characterIndexes = response.response.character;
        this.http.post('https://esi.evetech.net/latest/universe/names/?datasource=tranquility', characterIndexes)
        .subscribe( (charactersInfo: any[]) => {
           // do some stuff with this.characterIndexes and this.characters = [];
        });
      } else {
        this.characterIndexes = [];
        this.characters = [];
      }
    });

    searchEmptyTrigger.subscribe( () => {
      // reset values 
      this.characterIndexes = [];
      this.characters = [];
    });

PS: Я также открыт для альтернативного подхода, который выполняет те же операции, что и код выше, где я могу отменить http-запрос.

Всего 3 ответа


Я думаю, вы должны использовать takeUntil(searchEmptyTrigger) .

this.http.post('https://esi.evetech.net/latest/universe/names/?datasource=tranquility', characterIndexes)
        .pipe(takeUntil(searchEmptyTrigger)) // make sure to cancel the post if `searchEmptyTrigger` emits
        .subscribe( (charactersInfo: any[]) => {
           // do some stuff with this.characterIndexes and this.characters = [];
        });

PS, вы можете полностью упростить весь код в гораздо меньшем количестве кода.

Держитесь подальше от subscription внутри другой subscription , например, вашего post запроса.

Вы можете объединить их с помощью операторов. Я ленив написать все это: d


Попробуйте этот путь ...

Пример . Концепция состоит в том, чтобы unsubscribe на наблюдаемый объект ...

const request = this.searchService.search(this.searchField.value)
  .subscribe(
    result => { this.result = result.artists.items; },
    err => { this.errorMessage = err.message; },
    () => { console.log('Completed'); }
  );

  request.unsubscribe();  // <-- Hear you can cancel the API request.. 
  //Just set in when you need to cancel. It will works fine. 
  // E.x use with timeout or delay option of observable. 
}

Кажется, я понял это. Сначала я попытаюсь объяснить, что пошло не так, после чего я перейду через свое решение.

Эта проблема

В коде вопроса возникла проблема. Прежде чем новое значение было испущено, я уже выполнил бы http-запрос в searchTrigger , но не в searchTriggerEmpty . Поэтому searchTrigger может быть испущен после того, как searchTriggerEmpty даже жесткий searchTrigger .

Решение

Решение заключалось в том, чтобы исправить порядок испускания. Для этого событие должно быть выпущено до того, как будут сделаны какие-либо запросы. И когда запускается новое событие, предыдущее событие должно быть отменено ( switchMap ).

Код объяснен

  • searchTrigger испускает события, когда кто-то вводит в searchBox .
  • Некоторые трубы добавлены. debounceTime( 500 ) и distinctUntilChanged() предотвращают слишком частое инициирование события.
  • Канал map возвращает типы значений в searchBox.
  • switchMap имеет условное утверждение внутри него. Здесь выбирается лучший способ продолжения.
  • Если строка длиннее 2 символов, она вернет наблюдаемый массив символов (он получает их, выполняя некоторые HTTP-запросы и обрабатывая их).
  • Если строка короче, чем 2 символа, она будет напрямую возвращать пустой массив.
  • Причина использования switchMap здесь заключается в том, что switchMap ожидающий процесс будет отменен при switchMap нового значения.
  • Подписавшись на searchTrigger мы можем обработать последние результаты поиска.
  • Я также предоставил функцию, которую я использовал для process_searchString() чтобы вы лучше поняли, что она делает / возвращает.

Код

let searchBox = document.getElementById('search-box');
let searchTrigger = fromEvent(searchBox, 'input')
.pipe(
  debounceTime( 500 ),
  distinctUntilChanged(),
  map((event: any) => event.target.value ),
  switchMap( searchString => {
    if( searchString.length > 2 ){
      return this.process_searchString( searchString );
    } else if ( searchString.length <= 2 ) {
      return of( [] );
    }
  }),
);

searchTrigger.subscribe( ( characterIndexes: number[] ) => {
  this.characterIndexes = characterIndexes;
  this.characters = [];
  if( characterIndexes.length > 0 ){
    this.load_10characters();
  }
});

Отказ от ответственности: На момент написания этого я только что научился использовать rxjs. Итак, если вы видите что-то, что должно быть улучшено, то напишите комментарий.

PS: Для тех, кого это интересует, это объясняет, что switchMap лучше, чем я могу (и тоже развлекаюсь): https://www.youtube.com/watch?v=rUZ9CjcaCEw

Extra process_searchString() :

  private process_searchString( searchString: string ): Observable<number[]>{
    return new BehaviorSubject( searchString )
    .pipe(
      concatMap( ( text: string ) => this.request_characterSearch( text ) ),
      concatMap( ( response: any ) => {
        if( response.character ){
          return this.request_characterNames( response.character )
        } else {
          return of([]);
        }
      }),
      map( (charactersInfo: any[]) => this.sort_alphabetically( charactersInfo ) ),
      map( (charactersInfo: any[]) => charactersInfo.map( characterinfo => characterinfo.id ) ),
    );
  }

Есть идеи?

10000