可观察对象: 在新的订阅调用时取消之前的http请求

32

我正在为我的项目开发搜索功能。一旦用户在搜索栏中输入任何内容,在搜索文本发生任何更改时,我将向后端发送文本进行验证,并接收响应(文本是否存在错误):

this.searchBar.on('change', () => {

    http.post('api_link', {searchText: 
       this.serachBar.searchText}).subscribe(resp => {
            this.resp = resp['Result'];
       });
    })

现在,当用户不断在搜索栏中输入时,多个验证响应通过后端进入。然而,在任何新更改中,只有最新的订阅是有效的,并且对 API 的任何先前调用都是无效的。

是否有任何方法可以在对 subscribe 的任何新调用上取消之前的订阅?

注意:等待所有响应似乎是可能的,但我还会在搜索栏下方显示响应(同时显示一个加载器)。因此,我希望加载器保持加载状态,直到最新的响应可用为止,而不是在各种响应状态之间转换。


2
请了解switchMap和debounce。 - jonrsharpe
1
您可以在每次新更改时对订阅使用unsubscribe()。这样旧的HTTP请求将被取消,只有最新的“subscription”将有效。 - Yousef khan
一个非常好的资源,可以帮助你在未来:https://www.learnrxjs.io/ - Florian
1
https://stackoverflow.com/questions/53408051/wait-for-all-pending-request-resolved-and-take-value-from-last - martin
2个回答

40

我会使用一个subject来保持一切响应式。在你的模板html中监听变化事件,然后向subject发出新值。

 <searchBar (change)="search$.next($event.target.value)" />

然后在您的组件中:

  this.subscription = this.search$.pipe(
     debounceTime(800), 
     distinctUntilChanged(),
     switchMap(searchText=>http.post('api_link', {searchText})
    }).subscribe(response=>{
       this.response = response.
    });

如果subject发出新值,switchMap会取消任何尚未完成的HTTP请求。您可以尝试调整debouneTime以找到适合您的设置。最后,在ngOnDestroy中确保取消订阅您的subject,这将防止任何内存泄漏并保持所有内容良好和高效。

ngOnDestroy(){
   this.subscription.unsubscribe();
}

Suresh的答案中使用了distinctUntilChanged(),是这个解决方案的一个非常好的补充。我正在添加它,但想要在下面给予他答案的信用。这样做的好处是,如果我搜索egg,则会进行请求。但如果我在debounce完成之前添加s到egg的末尾并改变主意,则不会再进行一个带有搜索egg的重复HTTP请求。

实际上我的元素是在TypeScript中动态创建的,所以我无法在模板中添加更改事件监听器。有什么方法可以仅在ts中完成这个操作?(如问题中所述,我已经在ts中为此添加了更改事件监听器) - D_S_X
@VSharma 这可能超出了原始问题的范围。不过,您是如何动态创建元素的呢? - JoshSommer
1
如果您的代码中有搜索栏类,您可以直接订阅更改事件发射器:this.searchBar.change.pipe(而不是this.search$.pipe(这是因为Angular的事件发射器扩展了RxJS的主题。我曾经读到过Angular团队不建议直接订阅事件发射器,但我可能记错了,您可能需要进行一些调查来确认这一点。这不是我通常遇到的情况,所以我没有必要担心它。 - JoshSommer
我们可以在拦截器中做这个吗? - chirag sorathiya

14

你需要使用 debounceTime 和 switchMap 操作符。

this.searchBar.on('change', () => {

    of(this.serachBar.searchText).pipe(
       debounceTime(400),
       distinctUntilChanged(),
       switchMap((text)=> {
          return http.post('api_link', {searchText: text}).map(resp => {
            return resp['Result'];
          });
        });
    ).subscribe(response=> console.log(response));

});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接