Évitez les fuites de mémoire avec des stratégies de unsubscribe

Vos Observables sont très puissants : par conséquent, ils peuvent également être dangereux. Si votre application maintient une souscription à un Observable qui ne sert plus, vous générez une fuite de mémoire. En plus, ces fuites deviennent vite nombreuses et peuvent créer de gros problèmes de performance, même jusqu'à faire planter le navigateur !

Donc, il vous faut absolument implémenter une stratégie pour unsubscribe (se désabonner) des Observables auxquels vous avez souscrit. Dans ce chapitre, vous découvrirez les trois stratégies qui permettent d'empêcher toute fuite de mémoire potentielle causée par un Observable.

Trouvez la fuite

Vous allez maintenant créer une fuite de mémoire (mineure, ne vous inquiétez pas !) pour voir comment ces fuites peuvent vite devenir problématiques.

Danschapitre-4/home.component.ts, décommentez la ligne duconstructor:

export class HomeComponent {
  interval$ = interval(1000).pipe(
    tap(console.log)
  );

  constructor() {
    this.interval$.subscribe();
  }
}

Dans l'application, allez sur la page qui correspond à ce chapitre, et dans la console vous verrez les nombres qui augmentent. Jusqu'ici, rien d'étonnant.

Mais qu'est-ce qui se passe si vous passez sur la page d'un autre chapitre ? Et puis que vous reveniez ? Et si vous le faites plusieurs fois ?

Il y a visiblement plusieurs instances de l'Observable en parallèle !
Observables partout…

À chaque fois que leconstructordu component est appelé, une nouvelle instance de l'Observable est créée ! Imaginez ce que ça pourrait donner sur toute une application, avec plusieurs Observables, s'ils émettent des objets ou des tableaux… ça devient vite problématique ! 

Désabonnez-vous !

Si vous appelez la méthodesubscribe()d'un Observable dans votre code TypeScript, la meilleure façon d'éviter des fuites de mémoire est de vous assurer que cet Observable complète, car un Observable qui complète n'existe plus et ne peut créer de fuite.

Bien sûr, il faut uniquement que l'Observable complète si on n'en a plus besoin. Il y a trois cas principaux :

  • L'Observable ne sert que pour en afficher les émissions dans le DOM
    Dans ce cas, vous avez déjà la solution : le pipeasync  ! Ce n'est pas pour rien que vous vous en servez depuis le début du cours. Maintenant que vous connaissez les risques liés à une souscription mal gérée, sachez que le pipeasyncdétruit automatiquement la souscription au moment de la destruction du component. Vous pouvez donc vous en servir sans modération !
    Mais ce n'est pas toujours possible, par exemple lorsque vous souhaitez réagir programmatiquement aux émissions d'un Observable.

  • Vous savez d'avance combien d'émissions vous intéressent
    Souvent, seulement la première émission nous intéresse, ou les quelques premières. Pour ce cas de figure, vous avez l'opérateurtake.

  • Vous avez besoin des émissions de l'Observable pendant toute la durée de vie du component
    Dans ce cas, Angular fournit un opérateur spécial :takeUntilDestroyed.

Puisque vous avez déjà utilisé à plusieurs reprises le pipeasync, nous n'allons pas le revoir dans ce chapitre. Regardons plutôt les deux autres cas de figure.

Prenez une ou plusieurs valeurs

Le cas le plus simple est quand vous savez combien d'émissions vous intéressent. Souvent, vous voulez uniquement la première émission d'un Observable. À partir du moment où le nombre d'émissions utiles est connu, vous utiliserez l'opérateurtake().

take()prend un nombre comme argument, et complète l'Observable quand il a émis ce nombre de valeurs.

Créons un Observable à partir d'interval$qui s'arrêtera à 5 et souscrivons-y :

stopsAt5$ = this.interval$.pipe(
    take(6)
).subscribe();

Si vous regardez dans la console :

On voit 0, puis 1, puis 2, 3, 4, 5, puis … plus rien !
L'Observable complète après six émissions

Et voilà ! Votre Observable complète au bout de 6 émissions, et ne fuit plus !

Détruisez l'Observable en détruisant le component

L'autre cas de figure très courant de souscription TypeScript est quand vous voulez recevoir toutes les émissions d'un Observable pendant la durée de vie du component. Par conséquent, il faut trouver un moyen de compléter l'Observable au moment de la destruction du component. Pour cela, vous allez utiliser l'opérateur takeUntilDestroyed.

Créons un Observable qui sera détruit au moment de la destruction du component :

stopsOnDestroy$ = this.interval$.pipe(
    takeUntilDestroyed()
).subscribe();

Faites l'essai dans votre application en faisant les mêmes manipulations qu'au début du chapitre : allez sur la page de ce chapitre, attendez un peu, puis changez de page. Vous verrez l'Observable se compléter : il n'émet plus rien. Quand vous revenez sur la page de ce chapitre, une nouvelle instance de l'Observable est créée. Voici le résultat après quelques navigations :

On voit qu'il n'y a jamais plus d'une instance de l'Observable, et que celui-ci complète dès qu'on détruit le component.
L'Observable complète lors de la destruction du component

À vous de jouer

Contexte

Démontrez que vous savez éviter les fuites de mémoire !

Consigne

Créez trois Observables et assurez-vous qu'ils seront unsubscribe correctement :

  • le premier doit afficher les chiffres 0, 10, 20, 30… dans le DOM sans s'arrêter tant que l'utilisateur reste sur la page

  • le deuxième doit afficher les chiffres 0, 2, 4, 6, 8 et 10 — il doit s'arrêter à 10.

  • le dernier doit afficher les chiffres 0, 100, 200, 300… dans la console uniquement, sans s'arrêter tant que l'utilisateur reste sur la page

En résumé

  • Un Observable souscrit qui ne complète pas risque de créer des fuites de mémoire.

  • Les Observables souscrits avec le pipeasyncsont unsubscribe automatiquement par Angular lors de la destruction du component.

  • Si un Observable est souscrit avec la méthodesubscribe(), 2 possibilités : 

    • Si vous connaissez le nombre d'émissions qui vous intéressent, utilisez l'opérateur  take().

    • Si vous avez besoin de toutes les émissions durant la vie du component, utilisez l'opérateur  takeUntilDestroyed().

Maintenant que vous savez vous protéger contre les fuites de mémoire, passons à un sujet complexe mais puissant : les opérateurs haut niveau !

Et si vous obteniez un diplôme OpenClassrooms ?
  • Formations jusqu’à 100 % financées
  • Date de début flexible
  • Projets professionnalisants
  • Mentorat individuel
Trouvez la formation et le financement faits pour vous