Как отменить http.post (), пока он ожидает ответа? - PullRequest
0 голосов
/ 30 октября 2018

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

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

Подобное происходит, когда срабатывает 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 ]

0 голосов
/ 30 октября 2018

Я думаю, вы должны использовать 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

0 голосов
/ 31 октября 2018

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

Проблема

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

Решение

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

Код объяснения

  • searchTrigger генерирует события, когда кто-то печатает в searchBox.
  • Добавлены некоторые трубы. debounceTime( 500 ) и distinctUntilChanged() предотвращают слишком частое срабатывание события.
  • Канал map возвращает типы значений в searchBox.
  • В switchMap есть условный оператор. Здесь выбирается лучший способ продолжить.
  • Если строка длиннее 2 символов, она возвращает наблюдаемый массив символов CharacterIndexes (он получает их, выполняя некоторые http-запросы и обрабатывая их).
  • Если строка короче, чем 2 символа, она будет напрямую возвращать пустой массив.
  • Причина использования 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 ) ),
    );
  }
0 голосов
/ 30 октября 2018

Попробуйте вот так ...

Пример: Идея заключается в 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. 
}
...