Terminer un Observable RxJS avec un autre

— Nicolas Zozol

Comment terminer un observable quand un autre se termine ?


Quand utiliser RxJS ?

Je travaille sur des donnĂ©es temps rĂ©el en ce moment, et je commence Ă  ĂȘtre plutĂŽt bon en RxJS. J'ai pestĂ© pendant des annĂ©es contre RxJS parce qu'il Ă©tait livrĂ© avec Angular, et je ne vois pas l'intĂ©rĂȘt d'utiliser un Observable pour une requĂȘte REST, quand une Promise fait parfaitement l'affaire.

De plus, l'API de RxJS évoluait vite, ce qui agaçait quand on ne suivait pas ce projet de prÚs.

Avec des données temps réel, c'est une autre histoire. RxJS excelle, et son intégration avec Typescript est parfaite.

Terminer un observable

RxJS inclut de nombreux opĂ©rateurs, et au fil du temps certains ont Ă©tĂ© retirĂ©s pour limiter la surface de l'API, ce qui est une bonne chose. Écrire son propre opĂ©rateur est trĂšs facile : si votre code utilise souvent une mĂȘme combinaison d'opĂ©rateurs avec une logique rĂ©currente, n'hĂ©sitez pas Ă  la factoriser dans votre opĂ©rateur personnalisĂ©.

Dans mon projet, j'ai souvent besoin de terminer un Observable quand un autre se termine. Les deux finissent alors en mĂȘme temps. J'ai d'abord supposĂ© que le cas Ă©tait courant et qu'un opĂ©rateur existait donc dĂ©jĂ , mais takeUntil(other$) termine votre observable quand other$ dĂ©marre.

Terminer un Subject est facile aussi, avec subject.completes(), mais je préfÚre travailler avec des observables.

Mon opérateur personnalisé

Je veux que mon stream$ se termine avec le flux other$. Je souhaite l'utiliser ainsi :

stream$.pipe(completeWith(other$));

Ma fonction completesWith tient en 4 lignes de code :

export const completesWith =
  (other$: Observable<any>) => (o: Observable<number>) => {
    const terminator$ = new Subject();
    other$.subscribe().add(() => {
      terminator$.next('terminate');
    });
    return o.pipe(takeUntil(terminator$));
  };

Quand other$ se termine, un Subject terminator$ émet une valeur quelconque, et grùce à takeUntil, notre observable se termine à son tour.

Remplacer le Subject par un Observable

Comme je le disais, je préfÚre éviter les Subjects. La façon dont les Subjects stockent leurs listeners peut provoquer des fuites mémoire. Le style fonctionnel offre bien plus de garanties.

export const completesWith =
  (end$: Observable<any>) => (o: Observable<number>) => {
    const terminator$ = new Observable((subscriber) => {
      end$.subscribe().add(() => {
        // Will officially start the observable
        subscriber.next('FUNCTIONAL terminate');
      });
    });

    return o.pipe(takeUntil(terminator$));
  };

C'est plus un exercice qu'une réalité, car mon projet est plein de Subjects, mais ce sont toujours des objets privés, aux pouvoirs trÚs limités. Je les utilise simplement parce que c'est pratique et plus lisible. Bien qu'un peu dangereux...

Si vous ne pouvez pas remplacer votre Subject par un Observable, le mal est probablement dĂ©jĂ  fait. Votre Subject est peut-ĂȘtre liĂ© Ă  d'autres objets, avec une complexitĂ© qui met votre mĂ©moire en danger.

Articles liés