Mark
Sweep
marked=false
marked=false
marked=true
marked=true
marked=true
marked=true
@Component({
selector: 'app-root',
template: `<app-sub *ngIf="hide"></app-sub>`
})
export class AppComponent {
hide = false;
constructor() {
setInterval(() => this.hide = !this.hide, 50);
}
}
@Injectable()
export class DummyService {
behavior$ = new BehaviorSubject(2);
replay$ = new ReplaySubject(1);
private registeredComponents = [];
register(component) {
this.registeredComponents.push(component);
}
}
@Component({
selector:'app-sub',
template: 'mega {{rand}}'
})
export class SubComponent {
rand = Math.random();
rand2 = 0;
subject = new BehaviorSubject(42);
arr = [];
constructor(private dummyService: DummyService) {
for (let i = 0; i < 100000; ++i) {
this.arr.push(Math.random());
}
}
}
Reminder: SubComponent is shown / hidden periodically with *ngIf
@Component({
selector:'app-sub',
template: 'mega {{rand}}'
})
export class SubComponent {
rand = Math.random();
rand2 = 0;
subject = new BehaviorSubject(42);
arr = [];
constructor(private dummyService: DummyService) {
for (let i = 0; i < 100000; ++i) {
this.arr.push(Math.random());
}
this.subject.subscribe();
}
}
Reminder: SubComponent is shown / hidden periodically with *ngIf
@Component({
selector:'app-sub',
template: 'mega {{rand}}'
})
export class SubComponent {
rand = Math.random();
rand2 = 0;
subject = new BehaviorSubject(42);
arr = [];
constructor(private dummyService: DummyService) {
for (let i = 0; i < 100000; ++i) {
this.arr.push(Math.random());
}
this.subject.subscribe(() => {const blub = 34;});
}
}
Reminder: SubComponent is shown / hidden periodically with *ngIf
@Component({
selector:'app-sub',
template: 'mega {{rand}}'
})
export class SubComponent {
rand = Math.random();
rand2 = 0;
subject = new BehaviorSubject(42);
arr = [];
constructor(private dummyService: DummyService) {
for (let i = 0; i < 100000; ++i) {
this.arr.push(Math.random());
}
this.subject.subscribe(() => this.rand2 = 33);
}
}
Reminder: SubComponent is shown / hidden periodically with *ngIf
@Component({
selector:'app-sub',
template: 'mega {{rand}}'
})
export class SubComponent {
rand = Math.random();
rand2 = 0;
subject = new BehaviorSubject(42);
arr = [];
constructor(private dummyService: DummyService) {
for (let i = 0; i < 100000; ++i) {
this.arr.push(Math.random());
}
this.dummyService.behavior$.subscribe();
}
}
Reminder: SubComponent is shown / hidden periodically with *ngIf
@Component({
selector:'app-sub',
template: 'mega {{rand}}'
})
export class SubComponent {
rand = Math.random();
rand2 = 0;
subject = new BehaviorSubject(42);
arr = [];
constructor(private dummyService: DummyService) {
for (let i = 0; i < 100000; ++i) {
this.arr.push(Math.random());
}
this.dummyService.behavior$.subscribe(() => {const blub = 34;});
}
}
Reminder: SubComponent is shown / hidden periodically with *ngIf
@Component({
selector:'app-sub',
template: 'mega {{rand}}'
})
export class SubComponent {
rand = Math.random();
rand2 = 0;
subject = new BehaviorSubject(42);
arr = [];
constructor(private dummyService: DummyService) {
for (let i = 0; i < 100000; ++i) {
this.arr.push(Math.random());
}
this.dummyService.behavior$.subscribe(() => this.rand2 = 33);
}
}
Reminder: SubComponent is shown / hidden periodically with *ngIf
@Component({
selector:'app-sub',
template: 'mega {{rand}}'
})
export class SubComponent {
rand = Math.random();
rand2 = 0;
subject = new BehaviorSubject(42);
arr = [];
constructor(private dummyService: DummyService) {
for (let i = 0; i < 100000; ++i) {
this.arr.push(Math.random());
}
this.dummyService.register(this);
}
}
@Injectable()
export class DummyService {
behavior$ = new BehaviorSubject(2);
replay$ = new ReplaySubject(1);
private registeredComponents = [];
register(component) {
this.registeredComponents.push(component);
}
}
Reminder: SubComponent is shown / hidden periodically with *ngIf
ng
Component
window
Observable
ng
Component
window
Observable
Service
Subscribers
Subscribers
@Component({
selector:'app-sub',
template: 'mega {{rand}}'
})
export class SubComponent implements ngOnDestroy {
private destroy$ = new Subject<void>();
constructor(private dummyService: DummyService) {
this.dummyService.behavior$.pipe(
takeUntil(this.destroy$)
).subscribe((this => this.rand2 = 33));
this.dummyService.behavior$.pipe(
takeUntil(this.destroy$)
shareReplay(1),
takeUntil(this.destroy$)
).subscribe((this => this.rand2 = 33));
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
.subscribe() and shareReplay() must be preceded by takeUntil(this.destroy$)
npm install @angular-extensions/lint-rules --save-dev
Use the "angular-rxjs-takeuntil-before-subscribe" rule
from @angular-extensions/lint-rules
{
"extends": ["@angular-extensions/lint-rule"],
"rules": {
...
}
}
ng lint
tslint.sjon
The rule is strict and in some situations takeUntil(this.destroy$) would not be nessary, e.g.
this.httpClient.get('/something')
.subscribe(result => this.something = result);
this.dummyService.willFireOnce$
.subscribe(result => this.something = result);
this.dummyService.replay$.pipe(
first()
).subscribe(result => this.something = result);
However, these situations are hard to analyze statically and without the rule, refactorings can introduce memory-leaks if not re-evaluated