【问题标题】:How to use RxJS to cancel previous HTTP Requests in an Angular Service如何使用 RxJS 取消 Angular 服务中以前的 HTTP 请求
【发布时间】:2020-10-23 04:35:02
【问题描述】:

我知道有很多这样的问题,但它们经常让我感到困惑或难以适用于我的情况,所以我在这里问,以便我能把它弄明白。在我的组件打字稿文件中,我有一个调用服务的方法,该服务使用 http 返回 api 数据,因此当用户单击界面上的芯片时,他们调用以下内容..

fetchCounts(filters?: any): void {
    this.loaded = false;
    this.apiCall = this.api.getCounts(filters).subscribe((results: any) => {
        // do something with the returned data
        this.loaded = true;
        this.apiCall.unsunscribe()
    });
}

我的 api 服务如下所示:

getCounts(filters?: any): Observable<any> {
    let params: any;
    if (filters?.car?.length || filters?.bike?.length) {
      params = this.addObjectToParams(new HttpParams(), filters);
    }
    return this.http.get('api/counts', { params } )
      .pipe(map(this.extractData));
}

现在我注意到,当用户单击我的界面添加和删除进行 API 调用的芯片时,由于 API 调用过载/大量 API 调用,该界面似乎不再显示真实数据。因此,如果进行了新调用(如果再次调用 fetchCounts),我想取消当前的 api/http 调用。我曾尝试在getCountsmethod 中为.pipe 添加一个去抖,就像这样....pipe( debounceTime(500), map(this.extractData)); 但它似乎没有做任何事情。我想我必须添加一个switchMap,但是当我添加我的代码时,它会因为我缺乏理解而中断。我目前正在浏览文档...但我们将不胜感激。

【问题讨论】:

  • 能否请您说明如何调用fetchCounts() 函数?它是如何作为事件处理程序绑定到芯片的?与 HTML 模板一起显示。

标签: angular rxjs


【解决方案1】:

在这种情况下,您可以使用unsubscribe 方法简单地取消订阅之前的调用。 switchMap 用于我们有更高阶的 observable 时。

在拨打下一个电话之前取消订阅上一个电话。

fetchCounts(filters?: any): void {
    this.loaded = false;
    this.apiCall && this.apiCall.unsunscribe(); // Unsubscribe here as well.
    this.apiCall = this.api.getCounts(filters).subscribe((results: any) => {
        // do something with the returned data
        this.loaded = true;
        this.apiCall.unsunscribe();
    });
}

【讨论】:

  • 实现取消的好答案。我也认为 ops 应用程序的这种设计需要重构。本着 Web Api 的真正精神,所有调用都应该对响应时间和数据长度有严格的限制。如果需要长时间未完成的呼叫,则应禁止多次呼叫。在过去,异步调用不会被取消,只是被忽略了。
【解决方案2】:

如果您想避免处理订阅,可以让主题为您管理所有这些。这可能看起来像:

private _fetchCountsExe: Subject<any>;

constructor(){
  this._fetchCountsExe = new Subject<any>();
  this._fetchCountsExe.pipe(
    tap(_ => this.loaded = false),
    switchMap(filters => this.api.getCounts(filters))
  ).subscribe(results => {
    // do something with the returned data
    this.loaded = true;
  });
}

fetchCounts(filters?: any): void {
    this._fetchCountsExe.next(filters);
}

constructor() 中的代码可以放在任何在您的服务初始化时运行的地方。现在 switchMap 将完成取消旧订阅和切换到新订阅的所有工作。

这有很多好处。

其中最主要的是这种设计不依赖于 javascript 的事件循环来工作。考虑这个取消订阅 observable 的简单示例:

const subscription = from([1,2,3,4]).subscribe(_ => 
  subscription.unsubscribe();
);

这总是会失败。因为这个 observable 是同步运行的,subscription 仍然是未定义的,不能用于unsubscribe()

您当前的代码有一个隐藏的竞争条件,根据 Javascript 的事件循环的工作方式,该条件对您有效。但是,这会使您的代码变得脆弱。例如,如果您决定(将来)缓存最近的值并在它们新鲜足够的情况下立即返回它们,那么您的代码将意外中断。

这种方法还避免了将订阅置于仅在一个函数中使用的全局范围内。相反,您有一个描述动作的 Subject 和一个初始化动作的函数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-12
    • 2019-08-21
    • 1970-01-01
    • 2019-01-08
    • 1970-01-01
    • 2013-04-14
    • 1970-01-01
    相关资源
    最近更新 更多