DEV Community

LcsGa
LcsGa

Posted on • Originally published at linkedin.com

Les Signals Angular ne remplacent pas les Observables : Pull vs Push

Récemment, on voit de nombreux développeurs tenter de remplacer RxJS en créant leurs propres "opérateurs" pour les signals d'Angular. Cette démarche est compréhensible, mais elle peut entraîner des erreurs subtiles. Pour les éviter, il est essentiel de bien comprendre la distinction entre deux modèles de réactivité : Push et Pull.


Push (Observables) : Chaque donnée poussée compte

Dans le modèle Push, l’émetteur (la source) est aux commandes. Il émet des valeurs dans un flux "observable", et chaque valeur émise existe indépendamment. Ces valeurs sont ensuite traitées, une par une, par ceux qui les observent.

Imaginez une chaîne de montage : chaque pièce qui passe existe indépendamment. Si vous vous placez le long de la chaîne pour observer, vous verrez chaque pièce passer, sans exception. Si vous arrivez en retard, vous avez simplement raté les premières pièces. Elles ont été envoyées, mais elles ne vous attendent pas.

👉 Ce modèle est idéal pour gérer des flux de données, où chaque valeur a son importance.


Pull (Signals) : Seule la dernière valeur tirée compte

Dans le modèle Pull, c’est le consommateur qui est aux commandes. Une valeur n’a d’intérêt que lorsqu’elle est explicitement lue. Le consommateur "tirera" la valeur au moment où il en a besoin. Cela implique que les valeurs transitoires qui ne sont pas lues n’ont aucune importance.

Imaginez un tableau d'affichage numérique dans une gare. Il est constamment mis à jour, affichant des informations en temps réel sur les trains. Il peut y avoir des dizaines de mises à jour par seconde, mais le voyageur ne verra que la dernière version affichée lorsqu’il lève les yeux. Les mises à jour intermédiaires ne comptent pas pour lui. Seule la dernière version de l’état du tableau est pertinente.

👉 Les signals sont donc parfaits pour gérer un état dont seule la dernière version compte.


Pourquoi certains opérateurs ont du sens et d'autres non

Cette distinction entre Push et Pull est cruciale pour comprendre pourquoi certains opérateurs fonctionnent avec les signals et d’autres non.

✅ Ce qui fonctionne : opérer sur l'état final.

Les opérateurs qui ne se préoccupent pas de l’historique et réagissent uniquement au changement d’état final fonctionneront bien avec les signals. Par exemple, un opérateur double est parfait pour un signal. Il lit simplement la valeur actuelle, la multiplie par deux et retourne le résultat. Il n’a besoin de rien d’autre.

const count = signal(2);
const doubledCount = computed(() => count() * 2);
console.log(doubledCount()); // 4
Enter fullscreen mode Exit fullscreen mode

❌ Ce qui ne fonctionne pas : avoir besoin de l'historique.

Les opérateurs comme filter ou take sont conçus pour travailler avec des flux de données. Les adapter aux signals est risqué. Pourquoi ?

  • Le piège de filter :

    Imaginez un signal initialisé à 1 (const counter = signal(1)) et un signal résultant (un computed par exemple) qui ne garde que les nombres pairs. Si vous faites counter.set(2), puis counter.set(3), le signal résultant ne verra jamais la valeur 2. En effet, si vous ne tirez que la valeur finale, le signal prendra uniquement en compte la dernière valeur, qui est 3 (impair), et la filtrera. Ainsi, le résultat ne sera jamais celui que vous attendiez.

    const counter = signal(1);
    const evenCounter = computed(() => {
        const value = counter();
        return isEven(value) ? value : undefined;
    });
    
    counter.set(2);
    counter.set(3);
    console.log(evenCounter()); // undefined => instead of 2
    

    À l’inverse, si la dernière valeur est 4, vous aurez la fausse impression que votre opérateur fonctionne correctement.

    const counter = signal(1);
    const evenCounter = computed(() => {
        const value = counter();
        return isEven(value) ? value : undefined;
    });
    
    counter.set(2);
    counter.set(3);
    counter.set(4);
    console.log(evenCounter()); // 4 => tricky result
    
  • Le piège de take :

    De la même manière, avec le même signal counter initialisé à 1, si vous effectuez counter.set(2), puis counter.set(3), puis counter.set(4), un opérateur take(3) ne prendra en compte que la dernière valeur (4). Il n’aura aucune idée que les valeurs 1, 2 et 3 ont existé. Pour lui, c'est sa première (et unique) itération.


En résumé

Les signals sont optimisés pour gérer l’état de l’application (modèle Pull), tandis que RxJS est parfait pour gérer des flux de données (modèle Push).

Avant d’utiliser l’un ou l’autre, posez-vous la question suivante :

"Ai-je besoin de connaître l’historique des données, ou est-ce que seule la dernière valeur m’importe ?"

Si seule la dernière valeur compte, les signals sont faits pour vous. Sinon, un observable est probablement la meilleure solution.

Top comments (0)