【问题标题】:How to cancel ongoing HTTP requests when there's a new requests in angular 6 with rxjs 6当使用 rxjs 6 在 angular 6 中有新请求时如何取消正在进行的 HTTP 请求
【发布时间】:2019-01-08 06:05:15
【问题描述】:

我的应用中有以下两个 HTTP 服务 RxnsSearchServiceRxnsSearchHitCountService

使用forkJoin处理两个请求,如下代码所示。

constructor(
  private rxnsSearchService: RxnsSearchService,
  private rxnsSearchHitCountService: RxnsSearchHitCountService
) { }
const rxnsObservable: Observable<Array<any>> = this.rxnsSearchService.getReactions(this.searchParams, filters);
const headCountObservable: Observable<number> = this.rxnsSearchHitCountService.getHitCount(this.searchParams, filters);
forkJoin([rxnsObservable, headCountObservable]).pipe().subscribe((results) => { //handling results 
},
  error => {
    console.log(error);
  });

每当有新请求到来时,我想取消正在进行的旧请求。谁能帮我解决一下?

export class RxnsSearchService {
  sub: Subject<any> = new Subject();
  constructor(private httpClient: HttpClient) {}

  getReactions(params: Params, offset: number, perPage: number, filters: any) {
    const body = {
      filters: filters,
      query: params.query
    };
     return this.httpClient.post(environment.rxnsSearch, body).pipe(
      map((response: Array<any>) => {
        return response;
      }),
      catchError(error => {
        console.log(error);
        return throwError(error);
      })
    );
  }
}

export class RxnsSearchHitCountService {
  constructor(private httpClient: HttpClient) {}

  getHitCount(params: Params, filters: any) {
    const body = {
      filters: filters,
      query: params.query,
    };
    return this.httpClient.post(environment.rxnsSearchHitCount, body).pipe(
      map((response: number) => {
        return response;
      }),
      catchError(error => {
        console.log(error);
        return throwError(error);
      })
    );
  }
}

【问题讨论】:

  • 真正调用 forkJoin 的是什么?从您的代码 sn-p 看来,forkJoin 只被调用过一次。您能否提供其他代码来显示触发 forkJoin 的原因?谢谢!
  • this.route.queryParams.subscribe((params: Params) =&gt; { if (Object.keys(params).length &gt; 0) { this.displayreactions(); } }); } ngOnChanges(changes: SimpleChanges) { if (!changes['filters'].isFirstChange()) { this.displayreactions(); } }
  • forkJoin 从多个方法调用。
  • GitHub link LineNo:123

标签: javascript angular typescript rxjs


【解决方案1】:

我将通过一个简化的示例来介绍如何做到这一点的一般方法。假设我们目前有这个:

public getReactions() {
  this.http.get(…)
    .subscribe(reactions => this.reactions = reactions);
}

确保旧请求被取消的方法是在某些主题上发出:

private reactionsTrigger$ = new Subject<void>();

public getReactions() {
  this.reactionsTrigger$.next();
}

现在我们有了一个表示触发新请求的事件流的可观察对象。你现在可以像这样实现OnInit

public ngOnInit() {
  this.reactionsTrigger$.pipe(
    // Use this line if you want to load reactions once initially
    // Otherwise, just remove it
    startWith(undefined),

    // We switchMap the trigger stream to the request
    // Due to how switchMap works, if a previous request is still
    // running, it will be cancelled.
    switchMap(() => this.http.get(…)),

    // We have to remember to ensure that we'll unsubscribe from
    // this when the component is destroyed
    takeUntil(this.destroy$),
  ).subscribe(reactions => this.reactions = reactions);
}

// Just in case you're unfamiliar with it, this is how you create
// an observable for when the component is destroyed. This helps
// us to unsubscribe properly in the code above
private destroy$ = new Subject<void>();
public ngOnDestroy() {
  this.destroy$.next();
  this.destroy$.complete();
}

线

    switchMap(() => this.http.get(…)),

在您的情况下,实际上可能会将事件切换到forkJoin

    switchMap(() => forkJoin([rxnsObservable, headCountObservable])),

如果您希望单个事件流重新触发两个请求。

【讨论】:

  • 这其实是一个更好更完整的解释。我会在你的 switchMap 运算符中包含 forkJoin() 语句,以便它更适合所提出的问题,那么这可能是公认的答案
  • @ggradnig 谢谢,我已经更新了我的答案以添加这个。
  • @IngoBürk 当你解释了一般方法时,对于像我这样的 rxjs 新手来说,完成它会很棒。我的意思是将switchMapdebounce 结合起来,以消除快速打字伪影并最大限度地减少取消请求的数量。解决方案可能是:switchMap(val =&gt; timer(500).pipe(switchMap(() =&gt; this.getResults(val)))) 建议 here
【解决方案2】:

查看显示 HTTP 请求的实际触发器的代码 sn-p 会很有帮助,但它很可能是在点击时调用函数的 UI 组件。

使用 RxJS 6 解决此问题的方法是使用 Subject 接收点击事件,然后使用 switchMap 运算符取消未完成的请求以防止背压。这是一个例子:

private clickSubject$: Subject<void> = new Subject();

constructor() {
    this.clickSubject$
        .pipe(switchMap(() => forkJoin([rxnsObservable, headCountObservable])))
        .subscribe((results) => // handling)
}

onClick() {
    this.clickSubject$.next(undefined);
}

如果您有多个地方要执行 http 请求,则发送到主题中:this.clickSubject$.next(undefined);

【讨论】:

  • 谢谢@ggradnig,看来这会有所帮助,会做出改变。
  • @ggrading 我已经推送代码GitHub
  • forkJoindisplayreactions 方法行号:123
  • 非常感谢@ggrading,你真的节省了我很多时间。
【解决方案3】:

你可以简单地在 RxJs 中使用 debounce 操作符:

debounce(1000);

它提供了一种在发送任何请求之前设置延迟(以毫秒为单位)的方法,

该示例仅将 1000 毫秒内触发的所有请求替换为一个请求。

更多详情:

fromEvent(input, 'input').pipe(
       map((e: any) => e.target.value),
       debounceTime(500),
       distinctUntilChanged()
     ).subscribe(
       (data) => {
           this.onFilterTable({column: fieldName, data});
       }
     );

【讨论】:

  • 有时我的结果比预期的要长一点,同时用户改变了他的意见(过滤选项),这就是为什么我想取消请求并提出新的请求。
  • 你能帮忙用switchmap吗?
  • 我也有很多复选框,即使我是通过debounceTime 完成的。如果用户在一秒钟后发生变化,就会混淆新旧结果。
  • 我的意图是在新请求到来时取消旧请求。
  • debounceTime 应该排在前面,不需要先映射
猜你喜欢
  • 2018-12-12
  • 2016-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-17
  • 1970-01-01
  • 2019-05-02
相关资源
最近更新 更多