如何取消/取消订阅所有待处理的HTTP请求的角度4+

时间:2017-09-06 07:08:28

标签: angular typescript rxjs angular-http-interceptors

如何取消/中止所有挂起的HTTP请求angular 4 +。

有一种unsubscribe方法可以取消HTTP请求,但是如何一次取消所有待处理的请求。

特别是路线改变时。

我做了一件事

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

但如何全球实现

任何想法?

10 个答案:

答案 0 :(得分:34)

从RxJS签出takeUntil()运营商以全局删除您的订阅:

- RxJS 6+(使用pipe语法)

import { takeUntil } from 'rxjs/operators';

export class YourComponent {
   protected ngUnsubscribe: Subject<void> = new Subject<void>();

   [...]

   public httpGet(): void {
      this.http.get()
          .pipe( takeUntil(this.ngUnsubscribe) )
          .subscribe( (data) => { ... });
   }

   public ngOnDestroy(): void {
       // This aborts all HTTP requests.
       this.ngUnsubscribe.next();
       // This completes the subject properlly.
       this.ngUnsubscribe.complete();
   }
}

- RxJS&lt; 6

import 'rxjs/add/operator/takeUntil'

export class YourComponent {
   protected ngUnsubscribe: Subject<void> = new Subject<void>();

   [...]

   public httpGet(): void {
      this.http.get()
         .takeUntil(this.ngUnsubscribe)
         .subscribe( (data) => { ... })
   }

   public ngOnDestroy(): void {
       this.ngUnsubscribe.next();
       this.ngUnsubscribe.complete();
   }
}

每当您想要完成一堆流时,您基本上可以使用Subject在取消订阅next()上发出活动。在组件被销毁时取消订阅活动的Observable也是一种好习惯,以避免内存泄漏。

值得一读:

答案 1 :(得分:20)

您可以创建拦截器以将takeUntil运算符应用于每个请求。然后在路线更改时,您将发出取消所有待处理请求的值。

@Injectable()
export class HttpCancelInterceptor implements HttpInterceptor {
  constructor(private httpCancelService: HttpCancelService) { }

  intercept<T>(req: HttpRequest<T>, next: HttpHandler): Observable<HttpEvent<T>> {
    return next.handle(req).takeUntil(this.httpCancelService.onCancelPendingRequests())
  }
}

帮助服务。

@Injectable()
export class HttpCancelService {
  private cancelPendingRequests$ = new Subject<void>()

  constructor() { }

  /** Cancels all pending Http requests. */
  public cancelPendingRequests() {
    this.cancelPendingRequests$.next()
  }

  public onCancelPendingRequests() {
    return this.cancelPendingRequests$.asObservable()
  }

}

路线上的钩子会在您的应用中变更(例如应用组件)。

this.router.events.subscribe(event => {
  if (event instanceof ActivationEnd) {
    this.httpCancelService.cancelPendingRequests()
  }
})

答案 2 :(得分:9)

如果您不想手动取消订阅所有订阅,则可以执行以下操作:

export function AutoUnsubscribe(constructor) {

  const original = constructor.prototype.ngOnDestroy;

  constructor.prototype.ngOnDestroy = function() {
    for (const prop in this) {
      if (prop) {
        const property = this[prop];
        if (property && (typeof property.unsubscribe === 'function')) {
          property.unsubscribe();
        }
      }
    }

    if (original && typeof original === 'function') {
      original.apply(this, arguments)
    };
  };

}

然后你可以在组件中将它用作装饰器

@AutoUnsubscribe
export class YourComponent  {
}

但您仍需要将订阅存储为组件属性。 当您导出组件时,将发生AutoUnsubscribe功能。

答案 3 :(得分:2)

ngOnDestroy回调通常用于在销毁实例时需要进行的任何自定义清理。

你想在哪里取消你的请求?

如果您想要在浏览器关闭时取消您的请求,可能会有创意here

答案 4 :(得分:2)

试试这个:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs/Rx';

export class Component implements OnInit, OnDestroy {
    private subscription: Subscription;
    ngOnInit() {
        this.subscription = this.route.params.subscribe();
    }
    ngOnDestroy() {
        this.subscription.unsubscribe();
    }
}

答案 5 :(得分:2)

我不相信需要所需的功能,但您可以通过包装框架的http服务并委托给它来随时随地取消所有未完成的请求。

然而,当我们开始实施这项服务时,问题很快就会显现出来。一方面,我们希望避免更改现有代码,包括利用库存Angular http客户端的第三方代码。另一方面,我们希望避免实现继承。

为了充分利用这两个方面,我们可以使用我们的包装器实现 Angular Http服务。现有代码将继续工作而不进行更改(前提是所述代码不会像使用http instanceof Http那样做任何愚蠢的事情。)

import {Http, Request, RequestOptions, RequestOptionsArgs, Response} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import {Subscription} from 'rxjs/Subscription';



export default interface CancellationAwareHttpClient extends Http { }

export default class CancellationAwareHttpClient {
  constructor(private wrapped: Http) {
    const delegatedMethods: Array<keyof Http> = [
      'get', 'post', 'put', 'delete',
      'patch', 'head', 'options'
    ];
    for (const key of delegatedMethods) {
      this[key] = wrapped[key].bind(wrapped);
    }
  }

  cancelOutstandingRequests() {
    this.subscriptions.forEach(subscription => {
      subscription.unsubscribe();
    });
    this.subscriptions = [];
  }

  request(url: string | Request, options?: RequestOptionsArgs) {
    const subscription = this.wrapped.request(url, options);
    this.subscriptions.push(subscription);
    return subscription;
  }

  subscriptions: Subscription[] = [];
}

请注意,interface的{​​{1}}和class声明已合并。通过这种方式,我们的类通过CancellationAwareHttpClient声明的Http子句实现 interface

现在我们将提供我们的服务

extends

请注意我们如何覆盖现有框架提供的服务。我们使用工厂来创建我们的实例,并且不要为包装器本身添加任何DI装饰器,以避免在注射器中循环。

答案 6 :(得分:1)

您可以创建一个自定义Http服务(使用HttpClient)来维护待处理请求列表。每当你发起一个http我们这个自定义服务而不是Http / HttpClient时,现在将订阅推送到一个列表,并在返回响应时弹出该订阅。使用此选项,您将在列表中包含所有未完成的订阅。

现在在同一个自定义服务中在构造函数中注入路由器并订阅它以获取路由更改事件。现在只要这个observable发出,你需要做的就是取消订阅列表中的所有订阅并弹出它中的所有元素。

如果您需要代码段,请在评论中提及。

答案 7 :(得分:1)

    //This is the example of cancelling the get request once you leave the TestComponent.

    import { Component, OnInit} from '@angular/core';

    @Component({
      selector: 'app-test',
      templateUrl: './test.component.html'
    })
    export class TestComponent implements OnInit {

      request: any;
someList: any;

      constructor( private _someService: SomeService) {

      }

    ngOnInit() {
        this.getList();
      }

      ngOnDestroy(){
        this.request.unsubscribe(); // To cancel the get request.
      }

      getList() {
        this.request= this._someService.getAll()
          .subscribe((response: any) => {
            this.someList= response;
          }, (error) => {
            console.log("Error fetching List", error);
          })
      }

    }

答案 8 :(得分:0)

在@Bladito答案中添加一些内容,这几乎是完美的。

实际上,HttpCancelService堆栈是完美的,但是问题出在哪里。如果您有子路线,则在导航端调用此方法可能会导致问题。

因此,我制作了一个抽象的容器组件,该组件在销毁HttpCancelService时会调用它。这样一来,我便可以管理何时要以更精细的方式削减任何Http Canceling请求。

import { Component, OnDestroy, OnInit } from '@angular/core';
import { HttpCancelService } from '../../services/http-cancel-service.service';

@Component({
  selector: 'some-abstract-container',
  template: `
    ABSTRACT COMPONENT
  `,
  styleUrls: ['./abstract-container.component.scss']
})
export class AbstractContainerComponent implements OnInit, OnDestroy {
  constructor(protected readonly httpCancelService: HttpCancelService) {}

  ngOnInit() {}

  ngOnDestroy(): void {
    this.httpCancelService.cancelPendingRequests();
  }
}


有一个扩展抽象组件的具体组件:

import { Component, OnInit } from '@angular/core';
import { AbstractContainerComponent } from '../../../shared/components/abstract-container/abstract-container.component';
import { HttpCancelService } from '../../../shared/services/http-cancel-service.service';

@Component({
  selector: 'some-concrete-container',
  templateUrl: '.some-concrete-container.component.html',
  styleUrls: ['./some-concrete-container.component.scss']
})
export class SomeConcreteContainerComponent extends AbstractContainerComponent implements OnInit {
  constructor(protected readonly httpCancelService: HttpCancelService) {
    super(httpCancelService);
  }

  ngOnInit() {}
}

答案 9 :(得分:0)

我认为在路由更改级别取消请求不是一个好主意,因为这样会失去粒度。

例如,也许您想取消对一个组件的请求而不是另一个组件,因为它不会破坏。最重要的是,后台请求呢?调试为什么一些请求被随机取消将是非常棘手的。

但通常最好取消其组件将要销毁的 get 请求,不管路由更改


在销毁时取消订阅 observable

如果您想让生活更轻松,请使用 until-destroy。当您的组件将要被销毁 (ngOnDestroy) 时,它会自动取消订阅所有 observable。 它足够细化且更通用(不仅是 HttpRequests,而且所有 observables 都将被取消订阅)

import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
     
@UntilDestroy()
@Component({})
export class InboxComponent {
  ngOnInit() {
    interval(1000)
      .pipe(untilDestroyed(this))
      .subscribe();
  }
}