Un signal répond à « quelle est la valeur maintenant ? ». Un Observable répond à une autre question : « préviens-moi à chaque fois qu'une nouvelle valeur arrive ». C'est un flux de valeurs dans le temps auquel on s'abonne. C'est la brique de RxJS, et le langage natif d'Angular pour tout ce qui est asynchrone (HTTP, événements, temps réel).
HttpClient renvoie un Observable, Apollo aussi. Tu ne peux pas brancher leur API ni reviewer un composant qui charge des données sans savoir lire un subscribe. C'est le socle de la phase 3 (GraphQL).
On reprend notre fiche poste. Jusqu'ici elle affichait des notes. Maintenant on veut afficher la température live du poste : une nouvelle mesure arrive du capteur toutes les secondes. Ce n'est pas une valeur, c'est une suite de valeurs étalée dans le temps — exactement ce qu'un Observable modélise.
Un service nous expose ce flux. Le $ à la fin du nom est une convention RxJS qui signifie « ceci est un Observable » :
import { Observable } from 'rxjs';
export class PosteService {
// flux des mesures d'un poste : émet un number toutes les secondes
mesures$(id: string): Observable<number> {
return new Observable(sub => {
const t = setInterval(() => sub.next(lireCapteur(id)), 1000);
return () => clearInterval(t); // nettoyage quand on se désabonne
});
}
}
Trois choses qu'un Observable peut t'envoyer : des valeurs (next, autant qu'il veut), une erreur (error, qui termine le flux), ou un signal de fin (complete). Un flux de mesures, lui, ne se termine jamais tout seul : il émet à l'infini jusqu'à ce que toi tu coupes.
IEnumerable paresseux de LINQ
En C#, var q = nombres.Where(n => n > 0).Select(n => n * n); ne calcule rien : tu as juste décrit un pipeline. Le travail démarre au foreach (ou .ToList()). Un Observable, c'est pareil : tu le déclares, tu le composes… et rien ne tourne tant que personne ne s'abonne. Côté .NET tu as même l'équivalent exact, IObservable<T> (Rx.NET) : RxJS en est le cousin JavaScript. La différence avec IEnumerable : ici c'est le flux qui te pousse les valeurs quand elles arrivent (push), au lieu que tu les tires une par une (pull).
subscribe() — rien ne se passe sans abonnementUn Observable est paresseux et froid : tant que personne n'appelle subscribe(), le producteur (ici le setInterval) ne démarre même pas. S'abonner, c'est « ouvrir le robinet ». On branche le flux dans la fiche poste, et à chaque mesure reçue on met à jour un signal (qui, lui, alimente le template) :
import { Component, signal, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
export class FichePosteComponent implements OnInit, OnDestroy {
mesure = signal<number | null>(null);
private sub?: Subscription;
constructor(private postes: PosteService) {}
ngOnInit() {
// MAINTENANT le flux démarre. À chaque mesure → on écrit dans le signal
this.sub = this.postes.mesures$('A12').subscribe(m => this.mesure.set(m));
}
ngOnDestroy() {
this.sub?.unsubscribe(); // on coupe le robinet, sinon fuite mémoire
}
}
Template : <p>Température : {{ mesure() ?? '—' }} °C</p>. Remarque le pont entre les deux mondes : l'Observable gère le « ça arrive dans le temps », le signal gère le « voici la valeur courante à afficher ». subscribe prend aussi un objet à trois callbacks si tu veux gérer erreur et fin : .subscribe({ next, error, complete }).
Tu connais les Promise (leçon 03). Pourquoi Angular préfère les Observables pour les données ?
| Promise | Observable | |
|---|---|---|
| Nombre de valeurs | une seule | zéro, une, ou une infinité |
| Démarrage | immédiat (eager) | au subscribe (lazy) |
| Annulable ? | non | oui (unsubscribe) |
| Composable ? | peu | oui (opérateurs — leçon 09) |
Pour un appel HTTP qui renvoie une fois, les deux marchent. Mais dès qu'il faut annuler, réémettre ou transformer le flux (une recherche qui se relance à chaque frappe, par ex.), l'Observable gagne. D'où son omniprésence dans Angular.
mesure()) : il a toujours une valeur courante. Un Observable est un flux qui te pousse des valeurs quand elles arrivent : pas de « valeur courante » en soi, juste des événements. Règle pratique : RxJS pour orchestrer l'asynchrone (HTTP, debounce, annulation), signals pour l'état affiché. Le pont des deux mondes (toSignal) arrive en leçon 10.
subscribe sans désabonnement sur un flux qui ne se termine pas (mesures, événements, websocket) = fuite mémoire. L'abonnement survit au composant détruit et continue de tourner. Toujours unsubscribe (ou mieux : async pipe / takeUntilDestroyed, qu'on verra). (2) Oui, ce subscribe + unsubscribe manuel est verbeux — c'est volontairement la version « à la main » pour comprendre. Les opérateurs (leçon 09) et l'async pipe (leçon 10) font fondre ce code. (3) Ne subscribe jamais dans un autre subscribe (callback hell) : ça se compose avec des opérateurs.
unsubscribe sur un flux infini ?next (n valeurs), error (fin en erreur) ou complete (fin propre). Il est paresseux et froid : rien ne tourne avant subscribe() (comme un IEnumerable LINQ avant le foreach). Vs Promise : plusieurs valeurs, annulable, composable. Vs signal : l'Observable pousse l'asynchrone, le signal tient l'état affiché — ils coopèrent. Réflexe de lead : tout subscribe manuel sur flux non terminé doit être unsubscribe, sinon fuite mémoire.
RxJS — Observable (officiel) pour la mécanique next/error/complete, puis Angular — Making HTTP requests pour voir un Observable réel renvoyé par HttpClient. On enchaîne sur les opérateurs en leçon 09.
setInterval + abonnement), ou que je te montre côte à côte le même chargement de données en Promise vs Observable pour sentir la différence ? Demande-moi, ici, maintenant.