Common RXJS usage errors
Remember to unsubscribe
What is a Subscription? A Subscription is an object that represents a disposable resource, usually the execution of an Observable. A Subscription has one important method,unsubscribe, that takes no argument and just disposes the resource held by the subscription
An Observable that subscribes is destroyed to free memory only in the following cases
- Observable sends the value. Execute Observable.onComplete(). - Observable error occurs. Execute observable. OnError(). - The subscriber unsubscribes, subscription. Unsubscribe ().Copy the code
The first two are released automatically, while the last one requires the user to release it manually.
export class MemComponent implements OnInit {
constructor(private router: Router) { }
ngOnInit(): void {
// Each time a component is initialized, it listens for routing events and continues to listen after the component is destroyed, causing a memory leak.
this.router.events
.pipe(filter(e= > e instanceof NavigationEnd))
.subscribe(() = > {
console.log('End of navigation'); }); }}Copy the code
It’s clear that in most cases in ng projects we don’t need to release manually. Angular does a lot for us:
-
Angular implements Observables returned from Http Requests through HttpClient, and subscribes to these Observables to receive data returned from the API without calling unsubscribe().
export class XHRConnection implements Connection { constructor(req: Request, browserXHR: BrowserXhr, baseResponseOptions? : ResponseOptions) { this.request = req; this.response = new Observable<Response>((responseObserver: Observer<Response>) = >{... .const onLoad = () = > { const response = new Response(responseOptions); response.ok = isSuccess(status); if (response.ok) { responseObserver.next(response); // response sucess, then call complete to end current responseObserver responseObserver.complete(); return; } responseObserver.error(response); }; const onError = (err: ErrorEvent) = >{... .// response error, then call error to end current responseObserver responseObserver.error(newResponse(responseOptions)); }; }}Copy the code
-
Angular AsyncPipe does not need to call unsubscribe() to unsubscribe.
@Injectable(a)@Pipe({name: 'async'.pure: false}) export class AsyncPipe implements OnDestroy.PipeTransform {...private _subscription: SubscriptionLike|Promise<any>|null = null; .constructor(private _ref: ChangeDetectorRef) {} ngOnDestroy(): void { if (this._subscription) { this._dispose(); }}... .Copy the code
In addition to calling unsubscribe, we can use take, takeWhile, first,takeUntil in this example we use the more common takeUntil
export class MemComponent implements OnInit.OnDestroy { destory$ = new Subject(); constructor(private router: Router) { } ngOnInit(): void { this.router.events .pipe(filter(e= > e instanceof NavigationStart)) .subscribe(() = > { console.log('Navigation started'); }); this.router.events .pipe( filter(e= > e instanceof NavigationEnd), takeUntil(this.destory$) ) .subscribe(() = > { console.log('End of navigation'); }); } ngOnDestroy(): void { this.destory$.next(true); this.destory$.unsubscribe(); }}Copy the code
Don’t double subscribe
What are the problems with the following code?
/ / select box select$ = new Subject(); // Graph selection criteria chart$ = new Subject(); // Table selection criteria table$ = new Subject(); ngOnInit(): void { this.select$.subscribe(() = > { this.chart$.subscribe(this.getChartData()) this.table$.subscribe(this.getTableData()) }) } Copy the code
If it’s not clear, you can analyze the following code
const child = document.querySelector('#child'); document.addEventListener('click'.() = > { child.addEventListener('click'.() = > { console.log('Child is clicked'); }); }); Copy the code
Copy the code