Le battage médiatique autour des signaux —
Les bases de ce que nous appelons aujourd’hui des « signaux » remontent aux années 1970. Sur la base de ces travaux, ils se sont popularisés dans différents domaines de l’informatique, les définissant plus spécifiquement vers les années 90 et le début des années 2000.
Dans le domaine du développement Web, ils ont d’abord tenté leur chance avec KnockoutJSet peu de temps après, les signaux sont passés au second plan dans (la plupart de) nos cerveaux. Il y a quelques années, plusieurs implémentations similaires ont vu le jour.
Avec des noms et des détails de mise en œuvre différents, ces approches sont suffisamment similaires pour être regroupées dans une catégorie que nous connaissons aujourd’hui sous le nom de Réactivité à grain finmême s’ils ont différents niveaux de mises à jour « fines » x « grossières », nous reviendrons bientôt sur ce que cela signifie.
Pour résumer l’histoire: Même s’il s’agit d’une technologie plus ancienne, les signaux ont déclenché une révolution dans la façon dont nous pensions à l’époque l’interactivité et les données dans nos interfaces utilisateur. Et depuis lors, chaque bibliothèque d’interface utilisateur (SolidJS, Marko, Vue.js, Qwik, Angular, Svelte, Wiz, Preact, etc.) en a adopté une sorte d’implémentation (sauf pour React).
Généralement, un signal est composé d’un accesseur (getter) et d’un setter. Le setter établit une mise à jour de la valeur détenue par le signal et déclenche tous les effets dépendants. Tandis qu’un accesseur extrait la valeur de la source et est exécuté par des effets chaque fois qu’un changement se produit en amont.
const [signal, setSignal] = createSignal("initial value");
setSignal("new value");
console.log(signal()); // "new value"
Afin d’en comprendre la raison, nous devons approfondir un peu ce qui Architectures API et Réactivité à grain fin en fait signifier.
Architectures API
Il existe deux manières fondamentales de définir des systèmes en fonction de la manière dont ils traitent leurs données. Chacune de ces approches a ses avantages et ses inconvénients.
- Tirer: Le consommateur envoie une requête ping à la source pour obtenir des mises à jour.
- Pousser: La source envoie les mises à jour dès qu’elles sont disponibles.
Tirer les systèmes doivent gérer les interrogations ou tout autre moyen de maintenir leurs données à jour. Ils doivent également garantir que tous les consommateurs de données seront supprimés et recréés une fois que de nouvelles données arriveront pour éviter état déchirant.
Déchirure d’État se produit lorsque différentes parties de la même interface utilisateur se trouvent à différentes étapes de l’état. Par exemple, lorsque votre en-tête affiche 8 publications disponibles, mais que la liste en contient 10.
Pousser les systèmes n’ont pas à se soucier de maintenir leurs données à jour. Néanmoins, la source ne sait pas si le consommateur est prêt à recevoir les mises à jour. Cela peut provoquer contre-pression. Une source de données peut envoyer trop de mises à jour dans un laps de temps inférieur à celui que le consommateur est capable de gérer. Si le flux de mise à jour est trop intense pour le récepteur, cela peut entraîner une perte de paquets de données (entraînant état déchirant encore une fois) et, dans des cas plus graves, même faire planter le client.
Dans tirer systèmes, le compromis accepté est que les données ne savent pas où elles sont utilisées ; cela amène le destinataire à prendre des précautions pour maintenir tous ses composants à jour. C’est ainsi que des systèmes comme Réagir travailler avec leur mécanisme de démontage/rendu autour des mises à jour et de la réconciliation.
Dans pousser systèmes, le compromis accepté est que l’extrémité réceptrice doit être capable de gérer le flux de mise à jour de manière à ne pas provoquer de crash tout en maintenant tous les nœuds consommateurs dans un état synchronisé. En développement web, RxJS est l’exemple le plus populaire d’un système push.
Le lecteur attentif aura peut-être remarqué que les compromis sur chaque système se situent aux extrémités opposées du spectre : alors que les systèmes pull sont efficaces pour planifier les mises à jour efficacement, dans les architectures push, les données savent où elles sont utilisées, ce qui permet un contrôle plus granulaire. C’est ce qui constitue une excellente opportunité pour un hybride modèle.
Architectures push-pull
Dans les systèmes Push-Pull, l’État dispose d’une liste d’abonnés, qui peut ensuite déclencher une nouvelle récupération des données en cas de mise à jour. La différence avec le push traditionnel est que la mise à jour elle-même n’est pas envoyée aux abonnés, mais simplement une notification indiquant qu’ils sont désormais obsolètes.
Une fois que l’abonné se rend compte que son état actuel est devenu obsolète, il récupérera alors les nouvelles données au moment opportun, évitant ainsi toute sorte de contre-pression et se comportant de la même manière que le mécanisme d’extraction. La différence est que cela ne se produit que lorsque l’abonné est certain qu’il y a de nouvelles données à récupérer.
Nous appelons ces données signauxet la façon dont ces abonnés sont incités à mettre à jour sont appelés effets. A ne pas confondre avec useEffect
qui est un nom similaire pour une chose complètement différente.
Réactivité à grain fin
Une fois que nous aurons établi l’interaction bidirectionnelle entre la source de données et le consommateur de données, nous disposerons d’un système réactif.
UN système réactif n’existe que lorsque les données peuvent informer le consommateur qu’elles ont changé, et que le consommateur peut appliquer ces modifications.
Maintenant, pour y arriver à grain fin deux exigences fondamentales doivent être remplies :
- Efficacité: Le système exécute uniquement le minimum de calculs nécessaires.
- Sans pépin: Aucun état intermédiaire n’est affiché lors du processus de mise à jour d’un état.
Efficacité dans les interfaces utilisateur
Pour vraiment comprendre comment les signaux peuvent atteindre des niveaux d’efficacité élevés, il faut comprendre ce que signifie avoir un accesseur. En gros, ils se comportent comme des fonctions getter. Avoir un accesseur signifie que la valeur n’existe pas dans les limites de notre composant – ce que nos modèles reçoivent est un getter pour cette valeur, et chaque fois que leurs effets s’exécutent, ils apporteront une nouvelle valeur à jour. C’est pourquoi les signaux sont des fonctions et non de simples variables. Par exemple, dans Solid :
import { createSignal } from 'solid-js'
function ReactiveComponent() {
const [signal, setSignal] = createSignal()
return (
<h1>Hello, {signal()}</h1>
)
}
La partie qui est pertinente pour les performances (et l’efficacité) dans l’extrait ci-dessus est celle qui prend en compte signal()
est un getter, il n’est pas nécessaire de réexécuter l’intégralité ReactiveComponent()
fonction pour mettre à jour l’artefact rendu ; seul le signal est réexécuté, garantissant qu’aucun calcul supplémentaire ne sera exécuté.
Interfaces utilisateur sans problèmes
Les systèmes non réactifs évitent les états intermédiaires en disposant d’un mécanisme de démontage/rendu. Ils jettent les artefacts contenant des données éventuellement obsolètes et recréent tout à partir de zéro. Cela fonctionne bien et de manière cohérente, mais au détriment de l’efficacité.
Afin de comprendre comment les systèmes réactifs gèrent ce problème, nous devons parler du Défi Diamant. C’est un problème rapide à décrire mais difficile à résoudre. Jetez un œil au schéma ci-dessous :

Faites attention au E
nœud. Cela dépend D
et B
mais seulement D
dépend de C
.
Si votre système réactif est trop impatient d’être mis à jour, il peut recevoir la mise à jour de B
alors que D
est toujours obsolète. Cela provoquera E
pour montrer un état intermédiaire qui ne devrait pas exister.
C’est facile et intuitif d’avoir A
déclenchez des mises à jour sur ses enfants dès que de nouvelles données arrivent et laissez-les tomber en cascade. Mais si cela arrive, E
reçoit les données de B
alors que D
est périmé. Si B
est capable de déclencher une mise à jour depuis E
, E
affichera un état intermédiaire.
Chaque implémentation adopte différentes stratégies de mise à jour pour résoudre ce défi. Ils peuvent être regroupés en deux catégories :
- Signaux paresseux
Où un planificateur définit l’ordre dans lequel les mises à jour auront lieu. (A
alorsB
etC
alorsD
et enfinE
). - Signaux impatients
Lorsque les signaux savent si leurs parents sont vicié, vérificationou faire le ménage. Dans cette approche, lorsqueE
reçoit la mise à jour deB
cela déclenchera une vérification/mise à jour surD
qui grimpera jusqu’à pouvoir assurer de revenir dans un faire le ménage l’État, permettantE
pour enfin mettre à jour.
Retour à nos interfaces utilisateur
Après cela, plongez dans quoi réactivité fine Cela signifie qu’il est temps de prendre du recul et de regarder nos sites Web et nos applications. Analysons ce que cela signifie pour notre travail quotidien.
DX et UX
Lorsque le code que nous avons écrit est plus facile à raisonner, nous pouvons alors nous concentrer sur les choses qui comptent vraiment : les fonctionnalités que nous proposons à nos utilisateurs. Naturellement, les outils qui nécessitent moins de travail pour fonctionner nécessiteront moins d’entretien et de frais généraux pour l’artisan.
Un système sans problème et efficace par défaut échappera au développeur lorsqu’il sera temps de construire avec lui. Cela imposera également une connexion plus élevée à la plate-forme via une couche d’abstraction plus fine.
Lorsqu’il s’agit d’expérience développeur, il y a aussi quelque chose à dire sur le territoire connu. Les gens sont plus productifs dans le cadre des modèles mentaux et des paradigmes auxquels ils sont habitués. Naturellement, les solutions qui existent depuis plus longtemps et qui ont résolu un plus grand nombre de défis sont plus faciles à utiliser, mais c’est en contradiction avec l’innovation. C’était un exercice cognitif lorsque JSX est apparu et a remplacé les mises à jour impératives du DOM par jQuery. De la même manière, un nouveau paradigme pour gérer le rendu provoquera un inconfort similaire jusqu’à ce qu’il devienne courant.
Aller plus loin
Nous en reparlerons plus en détail dans le prochain article, où nous examinerons de plus près les différentes implémentations de signaux (paresseux, impatients, hybrides), les mises à jour programmées, l’interaction avec le DOM et le débogage de votre propre code !
En attendant, vous pouvez me retrouver dans la section commentaires ci-dessous, sur 𝕏 (Twitter), LinkedIn, Ciel bleuou même youtube. Je suis toujours heureux de discuter, et si vous me dites ce que vous voulez savoir, je m’assurerai de l’inclure dans le prochain article ! À bientôt !

(ouais)
Source link