这是来自角度组件的一些代码。它是一个搜索组件,其中characterIndexes
是搜索结果的数组。
通过在搜索框中键入搜索内容来检索搜索结果,该搜索框根据内容触发searchtrigger
或searchEmptyTrigger
。得到第一个结果后,我必须执行另一个http.post()
来获取属于索引的名称,以便对它们进行排序(我从代码中省略了该部分)。然后将结果插入characterIndex
数组中。
触发searchEmptyTrigger
时也会发生类似的情况。 characterIndex
数组只是设置为空,不需要http请求。
我遇到的问题是,在某些情况下,当触发searchEmptyTrigger
时,searchtrigger
中的代码仍在运行(由于http请求导致的延迟)。
结果是characterIndexes
首先为空。然后在收到searchtrigger
中来自http请求的结果后,它将再次填充。
因此,最大的问题是:'如何在正在等待响应的情况下取消正在运行的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请求。
答案 0 :(得分:1)
我认为您应该使用/* Container */
.links {
display: flex;
flex-direction: column;
flex-wrap: wrap;
width: 100vw !important;
height: 90vh;
}
/* Links in Container */
.links a {
white-space: nowrap;
flex: 1;
margin: 5px 5px 0 20px;
}
。
takeUntil(searchEmptyTrigger)
PS,您可以将整个代码完全简化为更少的代码。
不要将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 = [];
});
放在另一个subscription
中,就像您的subscription
请求一样。
您可以使用运算符将它们合并。 我很懒惰,:d
答案 1 :(得分:0)
尝试这种方式...
示例::概念是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.
}
答案 2 :(得分:0)
我想我明白了。首先,我将尝试解释发生了什么问题,然后再解决问题。
问题
在问题的代码中有问题。在发出新值之前,我应该已经在searchTrigger
中执行了http请求,但是没有在searchTriggerEmpty
中执行了。因此,searchTrigger
可能会在searchTriggerEmpty
甚至是最先启动searchTrigger
的情况下发出。
解决方案
解决方案是确定发射顺序。为此,必须在发出任何请求之前发出事件。并且当触发新事件时,应取消上一个事件(switchMap
-behavior)。
代码说明
searchTrigger
时会发出事件
searchBox
。debounceTime( 500 )
和distinctUntilChanged()
防止事件触发过多。 map
管道返回searchBox中的值类型。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
额外的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 ) ),
);
}