How to cancel/unsubscribe all pending HTTP requests angular 4+

廉价感情. 提交于 2019-12-17 15:42:07

问题


How to cancel/abort all pending HTTP requests angular 4+.

There is an unsubscribe method to cancel HTTP Requests but how to cancel all pending requests all at once.

Especially while route change.

There is one thing I did

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

but how to achieve this globally

Any Ideas?


回答1:


Checkout the takeUntil() operator from RxJS to globally drop your subscriptions :

- RxJS 6+ (using the pipe syntax)

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 < 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();
   }
}

You can basically emit an event on your unsubscribe Subject using next() everytime you want to complete a bunch of streams. It is also good practice to unsubscribe to active Observables as the component is destroyed, to avoid memory leaks.

Worth reading :

  • Avoiding take until leaks

  • A great answer from seangwright




回答2:


You can create an interceptor to apply takeUntil operator to every request. Then on route change you will emit event that will cancel all pending requests.

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

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

Helper service.

@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()
  }

}

Hook on route changes somewhere in your app (e.g. onInit in appComponent).

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



回答3:


If you don't want to manually unsubscribe all subscriptions, then you can do this:

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)
    };
  };

}

Then you can use it as decorator in your component

@AutoUnsubscribe
export class YourComponent  {
}

but you still need to store subscriptions as component properties. And when you navigating out of component, AutoUnsubscribe function will occurs.




回答4:


I'm not convinced of the need for the functionality requested, but you can accomplish this, cancelling all outstanding requests whenever and wherever you wish by wrapping the framework's http service and delegating to it.

However, when we go about implementing this service, a problem quickly becomes apparent. On the one hand, we would like to avoid changing existing code, including third party code, which leverages the stock Angular http client. On the other hand, we would like to avoid implementation inheritance.

To get the best of both worlds we can implement the Angular Http service with our wrapper. Existing code will continue to work without changes (provided said code does not do anything stupid like use 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[] = [];
}

Note that the interface and class declarations for CancellationAwareHttpClient are merged. In this way, our class implements Http by virtue of the interface declaration's extends clause.

Now we will provide our service

import {NgModule} from '@angular/core';
import {ConnectionBackend, RequestOptions} from '@angular/http';

import CancellationAwareHttpClient from 'app/services/cancellation-aware-http-client';

let cancellationAwareClient: CancellationAwareHttpClient;

const httpProvider = {
  provide: Http,
  deps: [ConnectionBackend, RequestOptions],
  useFactory: function (backend: ConnectionBackend, defaultOptions: RequestOptions) {
    if (!cancellationAwareClient) {
      const wrapped = new Http(backend, defaultOptions);
      cancellationAwareClient = new CancellationAwareHttpClient(wrappedHttp);
    }
    return cancellationAwareClient;
  }
};

@NgModule({
  providers: [
    // provide our service as `Http`, replacing the stock provider
    httpProvider,
    // provide the same instance of our service as `CancellationAwareHttpClient`
    // for those wanting access to `cancelOutstandingRequests`
    {...httpProvider, provide: CancellationAwareHttpClient}
  ]
}) export class SomeModule {}

Note how we override the existing framework provided service. We use a factory to create our instance and do not add any decorators for DI to the wrapper itself in order to avoid a cycle in the injector.




回答5:


ngOnDestroy callback is typically used for any custom cleanup that needs to occur when the instance is destroyed.

where do you want to cancel your request?

maybe if you want cancel your requests on browser close there is creative idea here




回答6:


Try This :

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();
    }
}



回答7:


    //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:


You can make a custom Http Service (using HttpClient) which maintains a list of pending requests. Whenever you fire a http us this custom service instead of Http/HttpClient,now push the subscriptions to a list and on return of the response pop that subscription out. Using this you will have all the incomplete subscriptions in a list.

Now in the same custom service Inject router in the constructor and subscribe on it to get the route change events. Now whenever this observable emits, all you need to do is to unsubscribe all the subscriptions present in the list and pop all the elements from it.

If you need code snippet, do mention in comment.



来源:https://stackoverflow.com/questions/46068908/how-to-cancel-unsubscribe-all-pending-http-requests-angular-4

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!