typescript - Hur man avbryter/avprenumererar alla väntande HTTP-förfrågningar i Angular 4+
Hur man avbryter/stoppar alla väntande HTTP-förfrågningar i Angular 4+.
Det finns en unsubscribe-metod för att avbryta HTTP-förfrågningar men hur avbryter man alla väntande förfrågningar på en gång.
Speciellt under ruttändring.
Det finns en sak jag gjorde
ngOnDestroy() {
this.subscription.unsubscribe();
}
men hur man uppnår detta globalt
Några idéer?
Svar #1
Kolla in takeUntil()-operatorn från RxJS för att globalt avsluta dina prenumerationer :
- RxJS 6+ (använder pipe-syntaxen)
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();
}
}
Du kan i princip sända ett event på ditt avregistrerings-Subject med next() varje gång du vill avsluta en mängd strömmar. Det är också god praxis att avregistrera aktiva Observables när komponenten förstörs, för att undvika minnesläckor.
Värt att läsa :
Undvika att ta tills läckor
Ett fantastiskt svar från seangwright
Svar #2
Du kan skapa en interceptorn för att tillämpa takeUntil-operatorn på varje begäran. Sedan, vid ruttändring, kommer du att skicka ett evenemang som avbryter alla väntande begäranden.
@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()))
}
}
Hjälptjänst.
@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 på ruttförändringar någonstans i din app (t.ex. onInit i appComponent).
this.router.events.subscribe(event => {
if (event instanceof ActivationEnd) {
this.httpCancelService.cancelPendingRequests()
}
})
Och sist men inte minst, registrera interceptorn i din 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 { }
Svar #3
Om du inte vill avregistrera alla prenumerationer manuellt, kan du göra så här:
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)
};
};
}
Då kan du använda det som en dekoratör i din komponent
@AutoUnsubscribe
export class YourComponent {
}
men du måste fortfarande lagra prenumerationer som komponentegenskaper. Och när du navigerar bort från komponenten kommer AutoUnsubscribe-funktionen att inträffa.
