En informatique, la programmation réactive est un paradigme de programmation visant à conserver une cohérence d'ensemble en propageant les modifications d'une source réactive (modification d'une variable, entrée utilisateur, etc.) aux éléments dépendants de cette source.
La programmation réactive est née à partir du patron de conception observateur afin de corriger ses défauts[1], parmi lesquels :
La plupart des applications contemporaines sont réactives, c'est-à-dire qu'elles répondent, en calculant, à des événements qui leur parviennent[1].
Cela permet aux utilisateurs d'avoir une meilleure interaction avec le système, une réponse beaucoup plus rapidement et donc une satisfaction[3].
La réactivité est désormais présente partout, et principalement dans les interfaces graphiques[1].
Les applications réactives sont importantes aujourd'hui car elles doivent être :
Ce qui correspond aux exigences des utilisateurs d'aujourd'hui[6].
Chaque variable est avant tout initialisée lors de l'analyse des expressions et les dépendances entre les variables sont enregistrées, de sorte que, dès qu'une variable subit une mise à jour (événement), alors toutes les variables qui en dépendent, subissent elles aussi une mise à jour de leur valeur. Par la suite, elles peuvent, elles aussi, déclencher la mise à jour d'autres variables et ainsi de suite[1].
La programmation réactive fait alors en sorte que chaque expression soit correcte à tout instant[1].
Par exemple :
a := 50
b := a + 22
c := a + 6
d := b + c
Après avoir analysé les expressions, voici les résultats :
a | b | c | d |
---|---|---|---|
50 | 72 | 56 | 128 |
Et voici les dépendances :
a | b | c | d |
---|---|---|---|
a | a | b , c |
Supposons désormais que la valeur de a change et passe à 10.
En programmation réactive, puisque b et c dépendent de a, alors b et c vont être automatiquement mises à jour.
a | b | c | d |
---|---|---|---|
10 | 32 | 16 | 128 |
De la même manière, b et c viennent d'être mis à jour. Par conséquent, puisque d dépend de b et c, alors d subira lui aussi une mise à jour.
a | b | c | d |
---|---|---|---|
10 | 32 | 16 | 48 |
Il suffit d'une seule modification de valeur pour que la variable se mette à jour.
La programmation réactive est basée sur des concepts comme des variations des valeurs dans le temps, des flots d’événements pour modéliser les mises à jour discrètes, des suivis de dépendances, et des propagations automatiques du changement[1].
À tout instant t de l'exécution du programme, toutes les expressions du programme doivent rester correctes. Par conséquent, les valeurs des variables évoluent dans le temps au fur et à mesure de l'exécution du programme de manière automatique et discrète[1].
Lorsqu'une variable change de valeur, toutes les variables qui dépendent d'elle sont mises à jour, ce qui nécessite un événement / message.
Il est important en programmation réactive d'écouter tous les événements qui peuvent survenir.
Les dépendances entre les variables, directes ou indirectes, doivent être suivies. Si une nouvelle dépendance fait son apparition, alors elle devra être sauvegardée afin de pouvoir propager dans un futur proche, d'éventuelles mises à jour de valeurs[1].
Lorsqu'une variable subit une mise à jour, toutes les autres variables qui en dépendent doivent être informées afin qu'elles puissent se mettre à jour.
La propagation doit se poursuivre automatiquement jusqu'à ce que toutes les variables qui en dépendent directement ou indirectement soient à jour[1].
Les langages de programmation réactive entrent dans la catégorie des langages orientés flots de contrôle[7].
L’approche traditionnelle pour implémenter des applications réactives est le patron de conception observateur qui sépare les producteurs d’événements (observables) des consommateurs d’événements (observateurs)[1].
Il s'agit ici, à la fois d'utiliser de la programmation réactive et de la programmation orientée objet.
L'approche qui semble la plus naturelle est de remplacer pour chaque objet les méthodes et les attributs par des réactions. Ainsi, les attributs se mettront tout seuls à jour.
Par exemple, il est possible de faire un mutateur qui ne prend aucune valeur en paramètre et qui met à jour un attribut.
Le principe de cette approche est de faire en sorte de ne mettre à jour les valeurs qu'au moment où on les demande. Seules les entrées sont sauvegardées en mémoire et les sorties sont recalculées à chaque appel[11].
Recalculer les sorties à chaque fois qu'on les demande est trop inefficace, surtout lorsqu'on travaille avec de grosses données ou si les calculs sont coûteux. Le but de cette approche est d'utiliser une sorte de cache qui permet d'éviter de recalculer inutilement certaines valeurs. Par conséquent, dès qu'on modifie une donnée alors on invalide de manière que dès qu'on demandera la valeur, on sera obligé de recalculer. S'il n'y a eu aucune invalidation, alors on retourne la donnée du cache[12].
Il y a ici, deux approches différentes :
Pour des grosses structures de données, ou des données assez importantes, le fait d'écraser entièrement les données pour les mettre à jour est trop coûteux.
Au lieu de tout recalculer, cette approche consiste à bien cibler les données à mettre à jour pour ne se contenter de ne calculer à nouveau que ce qui a changé réellement[14].
Cette approche consiste à sauvegarder toutes les mises à jour (tous les changements de valeur) et d'attendre la demande d'une valeur pour appliquer toutes les mises à jour d'un coup associée à cette valeur[15].
La programmation réactive fonctionnelle est une approche de la programmation réactive[16].
Jonas Bonér, Dave Farley, Roland Kuhn et Martin Thompson, « Le Manifeste Réactif », (consulté le ).