Это некоторый код из углового компонента. Это компонент поиска, где 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
имеет условное утверждение внутри него. Здесь выбирается лучший способ продолжения. 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 ) ),
);
}