Getting started General
Components
Forms
Trends
Utilities
Migrate from v1
  Join us
  JavaScript Articles

JavaScript Publish/Subscribe : le guide clair pour subscribe, subscriber et Pub/Sub

JavaScript

Un regard direct sur le pattern publish/subscribe en JavaScript. Vous apprendrez à vous abonner à des événements, ce que fait réellement un subscriber, comment publish déclenche vos subscribers, et comment construire un petit utilitaire Pub/Sub puissant que vous pouvez intégrer dans n'importe quelle application.

Guide JavaScript publish/subscribe
Publish/Subscribe en JavaScript : subscribe, subscriber, publish et unsubscribe

Le modèle publish/subscribe (souvent abrégé en pub/sub) vous aide à découpler les fonctionnalités. Un composant publie un événement. Chaque subscriber qui s'est abonné à cet événement exécute son callback. Le publisher ne sait pas qui s'est abonné. Le subscriber n'a pas besoin de savoir qui a publié. Résultat : un code simple et testable.

Que signifie subscribe ?

S'abonner consiste à enregistrer un callback pour un événement nommé. Vous pouvez avoir un subscriber ou plusieurs subscribers pour le même événement. Quand quelqu'un publie cet événement, le callback de chaque subscriber s'exécute avec les données fournies.

// Minimal shape we aim for
// bus.subscribe(eventName, callback)
// bus.publish(eventName, ...args)

Un petit utilitaire Pub/Sub robuste

Voici une implémentation propre qui couvre les besoins réels : subscribe, publish, unsubscribe et subscribeOnce. Elle est petite, rapide et indépendante du framework.

export function createPubSub() {
  const subscribersByEvent = Object.create(null);

  function subscribe(eventName, callback) {
    if (typeof callback !== 'function') {
      throw new TypeError('subscriber callback must be a function');
    }
    if (!Array.isArray(subscribersByEvent[eventName])) {
      subscribersByEvent[eventName] = [];
    }
    subscribersByEvent[eventName].push(callback);

    // return unsubscribe handle for convenience
    return function unsubscribe() {
      const list = subscribersByEvent[eventName];
      if (!list) return;
      const index = list.indexOf(callback);
      if (index !== -1) list.splice(index, 1);
      if (list.length === 0) delete subscribersByEvent[eventName];
    };
  }

  function subscribeOnce(eventName, callback) {
    const unsubscribe = subscribe(eventName, function wrappedOnce(...args) {
      unsubscribe();
      callback(...args);
    });
    return unsubscribe;
  }

  function publish(eventName, ...args) {
    const list = subscribersByEvent[eventName];
    if (!list || list.length === 0) return 0;

    // copy to prevent mutation during publish from breaking iteration
    const currentSubscribers = list.slice();
    for (const subscriber of currentSubscribers) {
      try {
        subscriber(...args);
      } catch (error) {
        // In production, you might log this error. We keep the loop going
        // so one bad subscriber doesn't break others.
      }
    }
    return currentSubscribers.length;
  }

  function clear(eventName) {
    if (eventName) {
      delete subscribersByEvent[eventName];
    } else {
      for (const key in subscribersByEvent) delete subscribersByEvent[key];
    }
  }

  return { subscribe, subscribeOnce, publish, clear };
}

// Example usage
const bus = createPubSub();

const unsubscribeAlert = bus.subscribe('alert', (payload) => {
  console.log('Alert subscriber:', payload.message);
});

bus.publish('alert', { message: 'Hello, subscriber!' });

// Stop this subscriber later
unsubscribeAlert();

// Subscribe once
bus.subscribeOnce('ready', () => console.log('Ready fired once'));
bus.publish('ready');
bus.publish('ready'); // second call does nothing

Les subscribers dans l'interface utilisateur : un câblage simple

Un schéma courant consiste à garder les composants UI légers. Le formulaire publie des événements comme form:success ou form:error. Un système de toast s'abonne à ces événements et affiche des messages. Le formulaire n'importe pas le toast. Le toast n'importe pas le formulaire.

// form.js
import { bus } from './shared-bus.js';

async function submitForm(data) {
  try {
    await api.save(data);
    bus.publish('form:success', { message: 'Saved!' });
  } catch (error) {
    bus.publish('form:error', { message: 'Please try again.' });
  }
}

// toast.js
import { bus } from './shared-bus.js';

bus.subscribe('form:success', ({ message }) => showToast(message, 'success'));
bus.subscribe('form:error', ({ message }) => showToast(message, 'error'));

Unsubscribe et sécurité mémoire

Gardez toujours une poignée de unsubscribe. Supprimez le subscriber lorsqu'un composant est démonté ou qu'une page change. Cela évite les subscribers orphelins et les fuites mémoire.

const stop = bus.subscribe('tick', () => updateTime());

// later
stop(); // unsubscribe this subscriber

Conseils de pattern et bonnes pratiques

  • Nommez les événements de façon cohérente : utilisez des espaces de noms comme cart:add, user:login.
  • Retournez unsubscribe : chaque subscribe doit renvoyer une fonction unsubscribe propre.
  • Gardez les payloads simples : privilégiez les objets plats. Documentez les champs requis.
  • N'abusez pas du pub/sub : les appels de fonction directs sont très bien lorsque le couplage est voulu.
  • Gérez les erreurs par subscriber : un subscriber défaillant ne doit pas bloquer les autres.
  • Fournissez subscribeOnce : utile pour les moments de cycle de vie ponctuels comme ready.

Canaux typés (facultatif)

En TypeScript, vous pouvez typer les noms d'événements et les payloads pour sécuriser davantage les appels subscribe et publish.

type Events = {
  'alert': { message: string };
  'ready': void;
};

function createTypedPubSub>() {
  const bus = createPubSub();
  return bus as {
    subscribe(event: K, cb: (payload: E[K]) => void): () => void;
    subscribeOnce(event: K, cb: (payload: E[K]) => void): () => void;
    publish(event: K, payload: E[K]): number;
    clear(event?: keyof E): void;
  };
}

const typedBus = createTypedPubSub();
typedBus.subscribe('alert', (p) => console.log(p.message));
typedBus.publish('alert', { message: 'Typed and safe' });

Conclusion

Le modèle publish/subscribe vous offre une façon propre de relier les parties de votre application sans couplage fort. Avec un utilitaire léger, une API subscribe simple et une gestion attentive des subscribers, votre code reste flexible et maintenable.

Commencez à créer avec Axentix

Prêt à créer des sites web exceptionnels ? Commencez avec le framework Axentix dès aujourd'hui.

Commencer

Articles similaires