Les Observables JavaScript : Comprendre et Implémenter
Les Observables JavaScript représentent des objets capables de produire une séquence de valeurs au fil du temps, de manière synchrone ou asynchrone. Contrairement aux Promises qui ne gèrent qu’une seule valeur, les Observables peuvent émettre plusieurs valeurs successives et offrent la possibilité d’annuler l’abonnement pour éviter les fuites mémoire.
Contenu
Principe fondamental
Les Observables reposent sur le pattern Observer et fonctionnent selon un modèle producteur-consommateur. Le producteur ou « Observable » qui génère des valeurs et les envoie aux consommateurs ou « Observateurs » qui se sont abonnés via la méthode subscribe(). L’Observable émet trois types de notifications :
- next pour chaque nouvelle valeur,
- error en cas d’erreur,
- et complete pour signaler la fin du flux.
Les Observables sont « lazy », ce qui signifie qu’ils ne s’exécutent que lorsqu’un Observer s’y abonne. Cette caractéristique permet d’optimiser les performances en ne déclenchant le traitement que lorsque c’est nécessaire.
Implémentation complète d’un Observable avec une fonction ou une classe
Pour créer un Observable JavaScript sans bibliothèque externe, vous pouvez utiliser une fonction constructeur simple ou une classe comme dans l’exemple ci-dessous. En revanche , lors de vos tests supprimez ou commenter l’un ou l’autre.
// Créer la fonction Observable
function Observable(subscribe) {
this.subscribe = subscribe;
}
// Ou créer une classe Observable plutôt qu'une fonction
class Observable {
constructor(subscribe) {
this._subscribe = subscribe;
}
unsubscribe(observer) {
const unsubscribe = this._subscribe(observer);
return {
unsubscribe: typeof unsubscribe === 'function' ? unsubscribe : () => { }
};
}
}
// Créer un Observable qui émet des valeurs
const monObservable = new Observable(function(observer) {
// Émettre plusieurs valeurs
observer.next(1);
observer.next(2);
observer.next(3);
// Simuler une valeur asynchrone
setTimeout(() => {
observer.next(4);
observer.complete();
}, 1000);
// Fonction de nettoyage optionnelle
return function unsubscribe() {
console.log('Nettoyage effectué');
};
});
// Créer un Observer
const monObserver = {
next: function(valeur) {
console.log('Valeur reçue:', valeur);
},
error: function(erreur) {
console.error('Erreur:', erreur);
},
complete: function() {
console.log('Terminé !');
}
};
// S'abonner à l'Observable
monObservable.subscribe(monObserver);
Résultat
L’exécution de ce code affiche dans la console les valeurs émises successivement 1,2,3 puis une seconde plus tard le chiffre 4 s’affichera.
Valeur reçue: 1
Valeur reçue: 2
Valeur reçue: 3
(après 1 seconde)
Valeur reçue: 4
Terminé !
Cette implémentation montre comment un Observable peut émettre des valeurs de manière synchrone (1, 2, 3) puis asynchrone (4 après un délai), avant de signaler la fin du flux avec complete().
Exemple pratique avec un événement de clic
Les Observables excellent dans la gestion des événements DOM répétitifs. Voici comment créer un Observable pour observer les clics sur un bouton :
// Créer un Observable à partir d'événements DOM
function fromEvent(element, eventType) {
return new Observable(function(observer) {
const handler = function(event) {
observer.next(event);
};
// Ajouter l'écouteur d'événement
element.addEventListener(eventType, handler);
// Fonction de nettoyage pour supprimer l'écouteur
return function unsubscribe() {
element.removeEventListener(eventType, handler);
};
});
}
// Utilisation
const bouton = document.querySelector('#monBouton');
const clics$ = fromEvent(bouton, 'click');
// S'abonner aux clics
const subscription = clics$.subscribe({
next: (event) => {
console.log('Bouton cliqué !', event);
}
});
Dans cet exemple, chaque clic sur le bouton déclenche l’émission d’un événement à travers l’Observable. La fonction fromEvent encapsule la logique d’ajout et de suppression des écouteurs d’événements, garantissant ainsi un nettoyage approprié lors du désabonnement et évitant les fuites mémoire.
Avantages et cas d’usage
Les Observables offrent plusieurs avantages pour gérer les flux de données. Ils permettent de traiter des événements multiples au fil du temps, peuvent être annulés pour libérer les ressources, et supportent des opérateurs de transformation puissants comme map, filter ou merge. Ces caractéristiques les rendent particulièrement adaptés pour gérer les interactions utilisateur, les requêtes réseau en streaming, les WebSockets ou les mises à jour en temps réel.
Bien que les Observables ne soient pas encore natifs en JavaScript, une proposition TC39 vise à les intégrer au standard ECMAScript. En attendant, vous pouvez créer vos propres implémentations pour des cas simples ou utiliser des bibliothèques comme RxJS pour des fonctionnalités avancées.