🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
JS MASTER CLASS /// MASTER THE ENGINE /// BUILD LOGIC /// ASYNC PATTERNS /// JS MASTER CLASS /// MASTER THE ENGINE ///

Publish / Subscribe Pattern | JavaScript Tutorial - In-Depth Guide

Master Publish/Subscribe: building a topic-based event bus, how it fully decouples publishers from subscribers, comparing it directly to the Observer Pattern, and common pitfalls like memory leaks from forgotten subscriptions.

Total XP: 0|💻 javascript XP: 0

Skill Matrix

UNLOCK NODES BY LEARNING NEW TAGS.

System Hub

Core logic.

Quick Quiz //

In Publish/Subscribe, does a publisher need a direct reference to its subscribers?


🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.

The Publish/Subscribe (Pub/Sub) pattern takes the Observer Pattern a step further by introducing a central event bus, so publishers and subscribers never need any direct reference to each other at all — the architecture behind Node.js EventEmitter and countless messaging systems.

1Publish / Subscribe Pattern | JavaScript Tutorial - In-Depth Guide Part 1

Publish/Subscribe introduces a central event bus (or 'broker') that publishers send messages to by topic/event name, and subscribers listen to by that same name — neither side ever references the other directly.

+
const bus = createEventBus();
bus.subscribe('user:login', (user) => console.log('Logged in:', user));
bus.publish('user:login', { name: 'Ana' }); // logs 'Logged in: {...}'
localhost:3000
📣

Fully Decoupled Messaging

2Publish / Subscribe Pattern | JavaScript Tutorial - In-Depth Guide Part 2

A minimal event bus implementation just maps topic names to arrays of subscriber callbacks, and publishing a topic calls every subscriber registered for it.

+
function createEventBus() {
  const topics = new Map();
  return {
    subscribe(topic, callback) {
      if (!topics.has(topic)) topics.set(topic, []);
      topics.get(topic).push(callback);
    },
    publish(topic, data) {
      (topics.get(topic) || []).forEach((cb) => cb(data));
    },
  };
}
localhost:3000

A Minimal Event Bus

3Publish / Subscribe Pattern | JavaScript Tutorial - In-Depth Guide Part 3

Because publishers and subscribers never reference each other, you can add new features that react to existing events (like adding analytics tracking to a 'checkout:complete' event) without touching the code that publishes them at all.

+
// Existing checkout code never needs to change:
bus.publish('checkout:complete', order);

// A brand-new analytics module, added later, independently:
bus.subscribe('checkout:complete', (order) => trackPurchase(order));
localhost:3000

Adding Features Without Touching Existing Code

4Publish / Subscribe Pattern | JavaScript Tutorial - In-Depth Guide Part 4

Node.js's built-in EventEmitter class is a production-grade, widely-used implementation of exactly this pattern, supporting topic-based (event-name-based) subscription with additional conveniences like once() and error handling.

+
const { EventEmitter } = require('events');
const bus = new EventEmitter();
bus.on('data', (chunk) => process(chunk)); // subscribe
bus.emit('data', someChunk); // publish
localhost:3000

Node.js EventEmitter

5Publish / Subscribe Pattern | JavaScript Tutorial - In-Depth Guide Part 5

The same decoupling that makes Pub/Sub powerful also makes it easy to lose track of who's listening to what — always support and use unsubscribe, and consider naming conventions (like 'domain:action') to keep a growing set of topics organized.

+
// Centralize topic names to avoid typos and ease searching:
const TOPICS = {
  USER_LOGIN: 'user:login',
  CHECKOUT_COMPLETE: 'checkout:complete',
};
bus.subscribe(TOPICS.USER_LOGIN, handleLogin);
localhost:3000

Managing Complexity at Scale

6Step-by-Step Breakdown

Publish/Subscribe introduces a central event bus (or 'broker') that publishers send messages to by topic/event name, and subscribers listen to by that same name — neither side ever references the other directly.

Checkpoint: In Publish/Subscribe, does a publisher need a direct reference to its subscribers?

  • Yes, exactly like the classic Observer Pattern
  • No, both sides only interact through the shared event bus

A minimal event bus implementation just maps topic names to arrays of subscriber callbacks, and publishing a topic calls every subscriber registered for it.

Because publishers and subscribers never reference each other, you can add new features that react to existing events (like adding analytics tracking to a 'checkout:complete' event) without touching the code that publishes them at all.

Node.js's built-in EventEmitter class is a production-grade, widely-used implementation of exactly this pattern, supporting topic-based (event-name-based) subscription with additional conveniences like once() and error handling.

Checkpoint: Is Node.js's EventEmitter class an implementation of the Publish/Subscribe pattern?

  • Yes, .on()/.emit() correspond to subscribe/publish
  • No, it uses a completely unrelated architecture

The same decoupling that makes Pub/Sub powerful also makes it easy to lose track of who's listening to what — always support and use unsubscribe, and consider naming conventions (like 'domain:action') to keep a growing set of topics organized.

Next, we'll explore 'XSS Basics'.

Level Up 🚀

Advanced cheat sheets, SEO tricks, and interview prep for this topic.

Browser Support

ChromeSupported

Fully supported.

FirefoxSupported

Fully supported.

SafariSupported

Fully supported.

EdgeSupported

Fully supported.

Accessibility (A11y)

1Use Pub/Sub to Decouple Accessibility Announcements from Business Logic

Publishing a generic 'notification:shown' event that a dedicated accessibility module subscribes to (to update an ARIA live region) keeps announcement logic centralized and consistent, without scattering live-region-update code throughout every feature that needs to show a notification.

SEO Implications

  • 1

    No Direct SEO Effect

    Publish/Subscribe is an application-architecture pattern; SEO relevance is limited to enabling more maintainable, loosely-coupled code that reduces the risk of feature interactions breaking rendered content.

Best Practices

Centralize Topic Name Constants Instead of Scattering String Literals

Using shared constants for topic names avoids typo-based bugs (a mismatched string silently means a subscriber never fires) and makes it easy to find every publisher/subscriber for a given topic.

Always Support and Use Unsubscribe to Avoid the Same Memory Leak Risk as the Observer Pattern

A Pub/Sub bus that never releases stale subscriber references has exactly the same memory-leak risk as an Observer Pattern subject that never removes old observers.

Frequent Bugs

THE BUG

A typo in a topic name string (e.g. subscribing to 'user:logIn' while the publisher emits 'user:login') causes the subscriber to silently never fire, with no error raised anywhere.

THE FIX

Use centralized, shared constants for topic names instead of repeating string literals at every subscribe/publish call site, eliminating the possibility of a typo mismatch.

THE BUG

Subscribing to a topic inside a component or module that gets created repeatedly, without ever unsubscribing, causing the same memory-leak pattern as a forgotten event listener.

THE FIX

Store the subscription reference and explicitly unsubscribe during cleanup, exactly as you would for a DOM event listener.

Real-World Examples

Decoupling Order Processing from Notification and Analytics Modules

An e-commerce backend needed to trigger email notifications, SMS alerts, and analytics tracking whenever an order was placed, without the core order-processing code needing to know about any of those downstream concerns.

// order-service.js — knows nothing about notifications or analytics:
bus.publish('order:placed', order);

// notification-service.js — subscribes independently:
bus.subscribe('order:placed', (order) => sendConfirmationEmail(order));

// analytics-service.js — also subscribes independently:
bus.subscribe('order:placed', (order) => trackOrderPlaced(order));

Interview Prep

?Frequently Asked Questions

Pascual Vila

Pascual Vila

Frontend Instructor // Code Syllabus

Common Pitfalls & Errors

The Error //

A typo in a topic name causing a subscriber to silently never fire

const TOPICS = { ORDER_PLACED: 'order:placed' }; bus.publish(TOPICS.ORDER_PLACED, order);

The Solution //

Use shared, centralized topic name constants instead of repeated string literals.

Lesson Glossary

[01]Publish/Subscribe (Pub/Sub)

A pattern where a central event bus routes messages between decoupled publishers and subscribers.

Code Preview
bus.publish(topic, data)

[02]Event Bus / Broker

The central intermediary object that publishers and subscribers interact with, instead of each other directly.

Code Preview
createEventBus()

[03]Topic (Event Name)

A named channel that publishers send to and subscribers listen on.

Code Preview
'user:login'

[04]EventEmitter

Node.js's built-in class implementing the Pub/Sub pattern via on()/emit()/off().

Code Preview
require('events')

[05]Decoupled Architecture

A design where components interact without direct references to each other, easing independent evolution.

Code Preview
no direct references

Continue Learning