Skip to content

Différence entre Observer et Subscriber

Pourquoi ils sont confondus

Lors de l'apprentissage de RxJS, les termes "Observer" et "Subscriber" apparaissent et peuvent prêter à confusion. Ces deux termes sont étroitement liés, mais ils ont des rôles distincts.

Qu'est-ce qu'un Observer ?

Un Observer est une interface pour recevoir des données.

Définition de l'Observer

typescript
interface Observer<T> {
  next: (value: T) => void;       // Recevoir une valeur
  error?: (err: any) => void;     // Recevoir une erreur
  complete?: () => void;          // Recevoir l'achèvement
}

Rôle de l'Observer

  • Définit "comment" recevoir des données
  • Possède 3 fonctions de rappel
  • Décrit quoi faire

Qu'est-ce qu'un Subscriber ?

Subscriber est une classe qui implémente l'Observer et exécute le processus d'abonnement proprement dit.

Rôle du Subscriber

  • Exécute les rappels de l'Observer
  • Détient la Subscription (gestion de l'abonnement)
  • Gère le processus de désabonnement
  • Contrôle comment exécuter

Deux situations où le Subscriber apparaît

Le Subscriber apparaît en fait dans deux situations différentes. Cependant, il s'agit dans les deux cas du même objet Subscriber. Comprendre cette différence est essentiel pour éviter toute confusion.

Situation 1 : Lors de la création d'un Observable (reçu en tant que paramètre)

Lors de la création d'un Observable avec new Observable(), subscriber est passé en paramètre à la fonction constructeur.

typescript
import { Observable } from 'rxjs';

// Utiliser subscriber pour publier des valeurs (le type est Subscriber<number>)
const observable$ = new Observable<number>(subscriber => {
  subscriber.next(1);      // Publier une valeur
  subscriber.next(2);
  subscriber.complete();   // Notifier l'achèvement
});

Type du subscriber dans cette situation

  • Type : Subscriber<number>
  • Rôle : Utilisé pour publier des données à l'intérieur de l'Observable
  • Les développeurs l'utilisent directement (appel de next(), error(), complete())

Situation 2 : Lors de l'appel à subscribe() (Observer → Converti en Subscriber)

Lorsque vous appelez subscribe() sur un Observable, l'Observer passé par le développeur est converti en interne en un Subscriber.

typescript
// Le développeur passe une interface Observer
observable$.subscribe({
  next: value => console.log('Valeur suivante:', value),
  error: err => console.error('Erreur:', err),
  complete: () => console.log('Terminé')
});

// ↑ RxJS crée en interne un Subscriber comme suit
// const subscriber = new Subscriber(observer);
// Et ce subscriber est passé comme paramètre dans la situation 1

Traitement dans cette situation

  1. Le développeur passe une interface Observer
  2. RxJS enveloppe en interne l'Observer dans un Subscriber
  3. Ce Subscriber est passé à la fonction constructeur de l'Observable (= subscriber dans la situation 1)

Important : Il s'agit dans les deux cas du même objet Subscriber

Le `Subscriber` dans les situations 1 et 2 est le même objet

typescript
const observable$ = new Observable(subscriber => {
  // Ce subscriber est l'objet Subscriber lui-même
  // créé lors de l'appel à subscribe()
  subscriber.next(1);
});

observable$.subscribe({
  // Cet Observer est enveloppé dans un Subscriber,
  // et passé à la fonction constructeur ci-dessus
  next: v => console.log(v)
});

En d'autres termes :

  • Le développeur appelle subscribe(observer)
  • RxJS crée new Subscriber(observer)
  • Ce Subscriber devient l'argument de la fonction constructeur de l'Observable
  • Par conséquent, subscriber dans la situation 1 = Subscriber créé dans la situation 2

Illustration des relations

Différences observées dans le code

Exemple complet : De la création de l'Observable à l'abonnement

typescript
import { Observable } from 'rxjs';

// 1. Lors de la création de l'Observable : utiliser subscriber pour publier des valeurs
const observable$ = new Observable<number>(subscriber => {
  console.log('Abonnement démarré');
  subscriber.next(1);
  subscriber.next(2);
  subscriber.next(3);
  subscriber.complete();
});

// 2. Lors de l'abonnement : passer l'Observer (le Subscriber est créé en interne)
observable$.subscribe({
  next: value => console.log('Valeur suivante:', value),
  error: err => console.error('Erreur:', err),
  complete: () => console.log('Terminé')
});

// Sortie :
// Abonnement démarré
// Valeur suivante: 1
// Valeur suivante: 2
// Valeur suivante: 3
// Terminé

Dans cet exemple

  • subscriber au moment de la création : Utilisé pour publier des données à l'intérieur de l'Observable
  • Observer au moment de l'abonnement : Définit le processus de réception des données publiées

Lors du passage d'un Observer

typescript
import { interval } from 'rxjs';

const observer = {
  next: (value: number) => console.log(value),
  error: (err: any) => console.error(err),
  complete: () => console.log('Terminé')
};

// Passer l'Observer à subscribe
const subscription = interval(1000).subscribe(observer);
// En interne, un Subscriber est créé et exécute les callbacks de l'observer

Lorsque l'on passe des callbacks directement (Notation simplifiée)

typescript
// Cela crée également un Subscriber en interne
const subscription = interval(1000).subscribe(
  value => console.log(value),  // next
  err => console.error(err),    // error
  () => console.log('Terminé')  // complete
);

Flux d'opérations réel

Le diagramme de séquence suivant montre le flux allant de la création de l'Observable à l'abonnement et à la publication des données.

Points importants de ce flux :

  1. Lorsque subscribe(observer) est appelé, un new Subscriber(observer) est créé
  2. Ce Subscriber est passé en paramètre à la fonction constructeur de l'Observable
  3. Lorsque subscriber.next() est appelé dans la fonction constructeur, la fonction interne observer.next() est exécutée
  4. En d'autres termes, subscriber dans la situation 1 = Subscriber créé dans la situation 2

Points importants

L'Observer est le "plan", le Subscriber est l'"exécuteur"

  • Observer : Une interface qui définit comment recevoir des données
  • Subscriber : Une classe qui implémente l'Observer et exécute l'abonnement
  • Le Subscriber apparaît dans deux situations

Comprendre quand utiliser le Subscriber

  • Lors de la création d'un Observable : Utilisez le paramètre subscriber dans new Observable(subscriber => {...}) directement
  • Lors de l'abonnement : Lors de l'appel à subscribe(observer), un Subscriber est automatiquement créé en interne

Dans les deux cas, le Subscriber est le même objet avec les méthodes next(), error(), complete().

Modèles de confusion courants

❌ Mauvaise compréhension #1

"Le subscriber lors de la création d'un Observable et l'observer lors de l'abonnement sont deux choses différentes"

❌ Mauvaise compréhension #2

"Le Subscriber est une implémentation interne, donc les développeurs n'y touchent pas"

✅ Compréhension correcte

Faits importants :

  • L'Observer que le développeur passe dans subscribe(observer) est enveloppé en interne dans un Subscriber
  • Ce Subscriber est passé en paramètre à la fonction constructeur lors de la création de l'Observable
  • En d'autres termes, subscriber dans la situation 1 = Subscriber créé dans la situation 2 (même objet)

Comprendre avec un exemple complet

typescript
import { Observable } from 'rxjs';

// 1. Créer l'Observable (rien n'est exécuté à ce stade)
const observable$ = new Observable<number>(subscriber => {
  console.log('La fonction constructeur a été exécutée!');
  subscriber.next(1);
  subscriber.next(2);
  subscriber.complete();
});

// 2. S'abonner (la fonction constructeur est exécutée pour la première fois à ce moment)
observable$.subscribe({
  next: v => console.log('Reçu:', v),
  complete: () => console.log('Terminé')
});

// Sortie :
// La fonction constructeur a été exécutée!
// Reçu: 1
// Reçu: 2
// Terminé

Flux dans cet exemple :

  1. observable$.subscribe({...}) est appelé
  2. RxJS crée new Subscriber({next: ..., complete: ...})
  3. La fonction constructeur (subscriber => {...}) est exécutée avec ce Subscriber comme paramètre
  4. subscriber.next(1) est appelé dans la fonction constructeur
  5. En interne, observer.next(1) est exécuté et "Reçu: 1" est affiché

En d'autres termes :

  • subscriber dans la situation 1 ← ceci
  • Subscriber créé dans la situation 2 ← ceci
  • Même objet !

Résumé

Comparaison de l'Observer et du Subscriber

ObserverSubscriber
TypeInterfaceClasse (implémente Observer)
RôleDéfinit comment recevoir des donnéesExécute le processus d'abonnement réel
ContenuCallbacks next/error/completeObserver + gestion de la Subscription
Quand il apparaîtUniquement lors de l'abonnementLors de la création de l'Observable et de l'abonnement

Important : Relation dans les deux situations

SituationCe que le développeur gèreType réelRelation
Lors de la création de l'ObservableParamètre dans new Observable(subscriber => {...})Subscriber<T>Le développeur l'utilise directement
Lors de l'abonnementObjet passé à subscribe(observer)Observer<T> → Converti en interne en Subscriber<T>RxJS convertit automatiquement

Point central

  • L'Observer que le développeur passe dans subscribe(observer) est enveloppé dans un Subscriber en interne
  • Ce Subscriber est passé en paramètre à la fonction constructeur de l'Observable
  • Par conséquent, le Subscriber utilisé dans les deux situations est le même objet

Prochaines étapes

Maintenant que vous comprenez la différence entre l'Observer et le Subscriber, nous allons nous intéresser à la gestion des abonnements proprement dite.

Ressources de référence

Publié sous licence CC-BY-4.0.