Keep your memor(ies|y) safe!

@KwintenP

@KwintenP

Why should we care?

@KwintenP

🇧🇪 Kwinten Pisman

Freelance frontend architect

Meetup organiser

Co-founder Strongbrew

GDE for Angular

@KwintenP

@KwintenP

strongbrew.io

@KwintenP

blog.strongbrew.io

angular-checklist.io

Angular Profiler Devtools

Memory management in Javascript

@KwintenP

Datatypes

boolean

number

strings

objects

@KwintenP

The memory heap

@KwintenP

Memory allocation

@KwintenP

Javascript uses garbage collection

@KwintenP

What is garbage?

Reachability

@KwintenP

Size terminology

Shallow size

Retained size

@KwintenP

What is a memory leak?

@KwintenP

Memory which is no longer needed that is not released.

@KwintenP

@KwintenP

Error in the code,

not in the tools

@KwintenP

Blame game!

References

@KwintenP

DOM Nodes

@KwintenP

Title Text

@KwintenP

RxJS

@KwintenP

RxJS is programming with asynchronous data streams

- André Staltz

@KwintenP

[

]

Arrays

@KwintenP

Observable

@KwintenP

Anatomy of an Observable

Observable.create(
  (observer) => {
     observer.next('a');
  }
);

@KwintenP

👷🏻

Fn

😴

.subscribe( 🤓 )

💩

🏋🏾‍♂️

⌛️

😴

@KwintenP

askMrMeeseeksForHelp() {
    timer(0, 1000)
      .pipe(map(this.createMrMeeseeks))
      .subscribe(newMrMeeseeks => this.mrMeeseeks.push(newMrMeeseeks));
}

component.ts

<mr-meeseeks *ngFor="let item of mrMeeseeks" [mrMeeseeks]="item"></mr-meeseeks>

component.html

Mr meeseeks emulator

1s

1s

1s

1s

1s

Heap situation

@KwintenP

HelpMeMrMeeseeksComponent

timer$

MrMeeseeks

Component

import { timer } from 'rxjs';


timer(0, 1000)
    .pipe(...)
    .subscribe(...);

@KwintenP

👷🏻

Fn

😴

const 🛎 = interval.subscribe( 🤓 )

🛎 .unsubscribe()

⚰️

const 🛎 = interval.subscribe( 🤓 )

👷🏻

👷🏻

😵

👷🏻

Anatomy of an Observable

Clean up observables by unsubscribing

@KwintenP

Quiz time!

@KwintenP

@Component({
 ...
})
export class SomeComponent {
   
   constructor(private http: HttpClient,
               private router: Router,) {
   }
   

   someAction(val) {
      this.http.post('some-api', {val}).subscribe();
      this.router.navigate(...);
   }    
}

@KwintenP

askMrMeeseeksForHelp() {
    this.subscription = interval(1000)
        .pipe(map(this.createMrMeeseeks))
        .subscribe(newMrMeeseeks => this.mrMeeseeks.push(newMrMeeseeks));
}

askMrMeeseeksForHelpAgain() {
    this.mrMeeseeks = [];
    this.subscription = interval(1000)
        .pipe(map(this.createMrMeeseeks))
        .subscribe(newMrMeeseeks => this.mrMeeseeks.push(newMrMeeseeks));
}

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

component.ts

Mr meeseeks emulator

askMrMeeseeksForHelp() {
    this.subscription = interval(1000)
        .pipe(map(this.createMrMeeseeks))
        .subscribe(newMrMeeseeks => this.mrMeeseeks.push(newMrMeeseeks));
}








ngOnDestroy(): void {
    this.subscription.unsubscribe();
}
import { timer } from 'rxjs';


timer(0, 1000)
    .pipe(...)
    .subscribe(...);

@KwintenP

Fn

😴

🛎 .unsubscribe()

     🛎= interval$.subscribe( 🤓 )

😵

👷🏻

let 🛎= interval$.subscribe( 🤓 )

Fn

⚰️

👷🏻

⚰️

👷🏻

Anatomy of an Observable

Every execution needs to be unsubscribed

@KwintenP

Unsubscription strategies

@KwintenP

@Component({
   template: `<mr-meeseeks *ngFor="let item of mrMeeseeks" [mrMeeseeks]="item">
              </mr-meeseeks>`
})
export class HelpMeMultipleTimesMrMeeseeksComponent implements OnDestroy {
   mrMeeseeks: Array<MrMeeseeks> = [];

   stop$ = new Subject<void>();

   // ...
   
   ngOnInit() {
      timer(0, 1000)
            .pipe(
                 map(this.createMrMeeseeks),
                 takeUntil(this.stop$),
            )
            .subscribe(newMrMeeseeks => this.mrMeeseeks.push(newMrMeeseeks));
   }

   ngOnDestroy() {
       this.stop$.next();
   }    
}

takeUntil

@KwintenP

@Component({
   template: `<mr-meeseeks *ngFor="let item of mrMeeseeks" [mrMeeseeks]="item">
              </mr-meeseeks>`
})
export class HelpMeMultipleTimesMrMeeseeksComponent implements OnDestroy {
   mrMeeseeks: Array<MrMeeseeks> = [];

   stop$ = new Subject<void>();

   // ...
   
   ngOnInit() {
      timer(0, 1000)
            .pipe(
                 map(this.createMrMeeseeks),

            )
            .subscribe(newMrMeeseeks => this.mrMeeseeks.push(newMrMeeseeks));
   }

   ngOnDestroy() {
       this.stop$.next();
   }    
}
@Component({
   template: `<mr-meeseeks *ngFor="let item of mrMeeseeks" [mrMeeseeks]="item">
              </mr-meeseeks>`
})
export class HelpMeMultipleTimesMrMeeseeksComponent implements OnDestroy {
   mrMeeseeks: Array<MrMeeseeks> = [];

   stop$ = new Subject<void>();

   // ...
   
   ngOnInit() {
      timer(0, 1000)
            .pipe(
                 map(this.createMrMeeseeks),

            )
            .subscribe(newMrMeeseeks => this.mrMeeseeks.push(newMrMeeseeks));
   }



 
}
@Component({
   template: `<mr-meeseeks *ngFor="let item of mrMeeseeks" [mrMeeseeks]="item">
              </mr-meeseeks>`
})
export class HelpMeMultipleTimesMrMeeseeksComponent implements OnDestroy {
   mrMeeseeks: Array<MrMeeseeks> = [];



   // ...
   
   ngOnInit() {
      timer(0, 1000)
            .pipe(
                 map(this.createMrMeeseeks),

            )
            .subscribe(newMrMeeseeks => this.mrMeeseeks.push(newMrMeeseeks));
   }




}

Quiz time!

@KwintenP

@Component({
  ...
})
export class HelpMeMultipleTimesMrMeeseeksComponent implements OnDestroy {
   mrMeeseeks: Array<MrMeeseeks> = [];

   stop$ = new Subject<void>();

   // ...
   
   ngOnInit() {
      combineLatest(
        timer(0, 1000).pipe(
                 map(this.createMrMeeseeks),
                 takeUntil(this.stop$),
            ), 
        doesNotComplete$
      ).subscribe(newMrMeeseeks => this.mrMeeseeks.push(newMrMeeseeks));
   }

   ngOnDestroy() {
       this.stop$.next();
   }    
}

takeUntil(                        )

!

combineLatest

Nicholas Jamieson: https://blog.angularindepth.com/rxjs-avoiding-takeuntil-leaks-fb5182d047ef

Async pipe

  • Subscribes to the observable
  • Unsubscribes on destroy 
  • Triggers change detection
@Component({
   template: `<mr-meeseeks *ngFor="let item of mrMeeseeks$ | async"
             [mrMeeseeks]="item"></mr-meeseeks>`
})
export class HelpMeMultipleTimesMrMeeseeksComponent {
   mrMeeseeks$: Observable<Array<MrMeeseeks>> = [];

   // ...
   
   ngOnInit() {
      this.mrMeeseeks$ = timer(0, 1000)
            .pipe(
                 map(this.createMrMeeseeks)
            );
   }
}

@KwintenP

@Component({
   template: `<mr-meeseeks *ngFor="let item of "
             [mrMeeseeks]="item"></mr-meeseeks>`
})
export class HelpMeMultipleTimesMrMeeseeksComponent {


   // ...
   
   ngOnInit() {
        timer(0, 1000)
            .pipe(
                 map(this.createMrMeeseeks)
            );
   }
}
@Component({
   template: `<mr-meeseeks *ngFor="let item of "
             [mrMeeseeks]="item"></mr-meeseeks>`
})
export class HelpMeMultipleTimesMrMeeseeksComponent {


   // ...
   
   ngOnInit() {
      this.mrMeeseeks$ = timer(0, 1000)
            .pipe(
                 map(this.createMrMeeseeks)
            );
   }
}
@Component({
   template: `<mr-meeseeks *ngFor="let item of "
             [mrMeeseeks]="item"></mr-meeseeks>`
})
export class HelpMeMultipleTimesMrMeeseeksComponent {
   mrMeeseeks$: Observable<Array<MrMeeseeks>> = [];

   // ...
   
   ngOnInit() {
      this.mrMeeseeks$ = timer(0, 1000)
            .pipe(
                 map(this.createMrMeeseeks)
            );
   }
}

Lift architecture

@KwintenP

lift<R>(operator: Operator<T, R>): Observable<R> {
    const observable = new Observable<R>();
    observable.source = this;
    observable.operator = operator;
    return observable;
}

Lift method

@KwintenP

interval$

.map()

mapInterval$

- operator: map

- source: interval$

@KwintenP

interval$

mapInterval$

- operator: map

- source: interval$

.subscribe(🤓)

.subscribe(🤓)

MapSubscriber

Quiz time!

@Component({
 ...
})
export class SomeComponent {
  
   local$ = new Subject();
   
   constructor(private http: HttpClient,
               private router: Router,) {
   }
   
   ngOnInit() {
     local$.subscribe(
     // do something here
     )
   }

   someAction(val) {
     local$.next(val);
   }    
}

Quiz time!

@Component({
 ...
})
export class SomeComponent {
   
   constructor(private route: Activatedrout) {
   }
   
   ngOnInit() {
     route.subscribe(
     	// do something here
     );
   }
}
  • Remember to check the Memory tab of the Dev Tools
  • Unsubscribe to execute teardown logic
  • Every execution must be cleaned up
  • Use the async pipe as much as possible
  • Error and complete are end events

@KwintenP

Key takeaways

@KwintenP

References

@KwintenP

@KwintenP

Made with Slides.com