【问题标题】:How to cancel a pending request in an inner HTTP observable with RXJS?如何使用 RXJS 取消内部 HTTP 可观察的待处理请求?
【发布时间】:2019-02-08 16:44:48
【问题描述】:

更改页面时取消挂起的 HTTP 请求

我们有一个 Angular 服务,它有一个昂贵的 HTTP 查询,可供 3 个不同的消费者访问。每个消费者都可以随时修改此查询,发出新请求,并且所有其他消费者都必须使用新数据进行更新。

因为 HTTP 订阅在完成后立即关闭,所以我们使用带有行为主体的内部可观察模式来保持消费者之间的联系(见下文)。

那么问题是,当用户更改页面时,当前未决的 HTTP 响应无法取消。

通常我认为在后台丢弃 HTTP 请求不会有太大的问题……但除了它是一项昂贵的操作之外,我发现在用户之后是否确实解决了待处理的响应已返回页面,它将使用旧查询的数据更新消费者。

没有布宜诺。

服务调用

private dataSubject = new BehaviorSubject<MyData>(...);
public data$ = this.dataSubject.asObservable();
...

getData(): Observable<MyData> {
  if (this.dataSubject)
    return this.data$;
  } else {
    const http$ = this.http.post(...))
      .pipe(map(response => response as MyData),
        takeUntil(this.unsubscribe$)); // see tearDown() below
    http$.subscribe(
      (availableDevices: MyData) => {
        this.dataSubject.next(availableDevices);
      }
    );
    return this.data$;
  }
}

我试图在每个消费者在 ngDestroy() 期间调用的服务中创建一个拆卸方法,但除非我完成了流,否则它不起作用。但那时,当用户返回页面时,我无法再次重新启动流。

tearDown(): void {
  this.unsubscribe$.next();
  this.unsubscribe$.complete();
  // this.dataSubject.next(null);
  // this.dataSubject.complete(); -- breaks
}

我绝不是 RXJS 专家,如果我的整体设计有误,请随时指出。我怀疑我应该使用 switchMap()share 来防止两个消费者发出相同的请求;但由于这种可观察到的模式是半热的,我不确定正确的做法是什么。更不用说取消它了。

我们将不胜感激。

【问题讨论】:

  • 是的,所以既然你有behaviorSubject,你应该实际使用它。您应该使用 this.dataSubject.next(apiData) 将返回的数据推送到 behaviorSubject 中,而不是返回 this.data$。然后 3 个消费者订阅公共 data$,假设他们都订阅了,当在 getData() 方法中触发 dataSubject.next() 时,它们将被更新。让我知道这是否没有意义。本质上,这变成了一个 rxjs 数据存储,将其视为一个轻量级的状态,沿着 redux/ngrx 的路线,没有动作、reducers、连接器等的所有开销
  • @Budhead2004 我还没有用 this.dataSubject.next(availableDevices); ?

标签: angular http rxjs observable rxjs-pipeable-operators


【解决方案1】:

取消 http 调用最简单的方法是取消订阅。

const subscription: Subscription = this.http.post(...).subscribe(...);
subscription.unsubscribe();

您的teardown 方法很有用,通常用作垃圾收集和取消任何待处理的 rxjs 可观察对象。您可以在服务/组件/指令 ngOnDestroy 钩子中调用它,以确保没有可观察对象留在内存中。

但如果我没记错的话,Angular 处理 http observables,所以你不需要处理它们。一般来说,你只需要处理你自己生成的 observables,比如你的BehaviorSubject

【讨论】:

  • “取消任何待处理的 rxjs 可观察对象”。我在 OP 中可能没有更清楚——这就是我在这里想要做的。当用户更改页面时取消挂起的 observables,但它不是很有效。我认为这是一个问题是错误的吗?我知道当用户更改页面时,它将在后台解决。对我来说,这些昂贵的电话在被有效地丢弃时继续进行似乎是错误的。
  • 从技术上讲,您必须管理一个基本数据存储,您的BehaviorSubject。你是对的,这是一个问题。在此实现中,您的 http 调用不会自行取消页面更改。您必须添加一些监视页面更改的逻辑,并基于此需要取消您的 http 调用,就像我向您展示的那样。我对导航历史有一个要点。我认为您也可以在您的情况下使用该逻辑。在这里查看:gist.github.com/bgolyoo/206b83d4227c84dea6610a6765edee86
  • 好的,有道理。我认为我没有提到的更大问题是我在页面加载期间收到过多的 http 调用,只有少数会取消。一旦页面被加载并且它按照它应该的方式对每个操作进行单个 HTTP 调用,它就会被成功取消。我想我是在试图欺骗它,只是希望取消会取消所有这些,但老实说,在这一点上,我应该调查一下为什么我会接到这么多过多的电话。 (因此我对 share 和 switchMap 的思考)。
【解决方案2】:

我解决了使用带有 AbortSignal / AbortController 的 Promises(异步函数)的问题,因为在我的情况下,尽管主题已取消,但 http 请求并未取消。也许我犯了一个错误,或者它现在可以工作了。但是,带有 AbortController 的 Promise 也可以完成这项工作。

请参阅here 如何使用 AbortController。但是,使用 AbortController 时可能会出现浏览器兼容性问题。

您还可以使用 .toPromise() 将 observables 转换为 Promise,因此这应该不是问题。

【讨论】:

  • 这看起来很有希望,但现在我开始质疑我的担忧是否有必要哈哈。
  • 这取决于具体情况。在我们的应用程序中,用户可能会来回移动导致第二个(但不相等)长时间运行的 http 请求,然后由于第一个仍在运行,因此非常慢。所以你的担忧可能是有效的。
  • 一个很好的建议,但是在研究之后,当我可以在我的拆卸方法中使用取消订阅时,它有点过于沉重。
  • 同意,但是当我尝试取消订阅时,我实际上可以在网络下的 Chrome 开发工具中看到 http 请求仍处于待处理状态,尽管它已取消订阅。也许我在那里犯了一个错误。您可以签入您的应用并告诉我吗?
  • 对我所说的持保留态度——但对于大多数基本的 HTTP 请求,我可以使用 takeUntil(this.unsubscribe$) 的管道运算符。在我的 ngDestroy() 中,我说 this.unsubscribe$.next() (清除呼叫)和 this.unsubscribe$.complete (关闭流)。我上面提到的场景有点不同,因为我有一个内部 observable,所以我结合了从内部取消订阅以及使用 takeUntil() ——当我切换页面时,我确实看到它在开发控制台中取消。我遇到的更大问题是首先要进行多次 XHR 调用。
猜你喜欢
  • 1970-01-01
  • 2021-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多