How to cancel/unsubscribe all pending HTTP requests in Angular 4+
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 :
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() }})
And last but not least, register the interceptor to your app.module.ts:
import { HttpCancelInterceptor } from 'path/to/http-cancel.interceptor'; import { HTTP_INTERCEPTORS } from '@angular/common/http'; @NgModule({ [...] providers: [ { multi: true, provide: HTTP_INTERCEPTORS, useClass: HttpCancelInterceptor } ], [...] }) export class AppModule { }
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
@AutoUnsubscribeexport class YourComponent {}
but you still need to store subscriptions as component properties.And when you navigating out of component, AutoUnsubscribe function will occurs.