EN/CH Mode
BRUCE_FE JS Interview Notes - Implement Subscribe and Emit Pattern
Learn how to implement the Subscribe and Emit pattern in JavaScript, master the core concepts of event-driven programming, and enhance the decoupling and maintainability of frontend code.
EN/CH Mode
Lazy to read articles? Then watch videos!
Subscribe and Emit Pattern Overview
The Subscribe and Emit pattern (Pub/Sub Pattern) is a common design pattern that allows different components to communicate through an event mechanism without direct references to each other. This pattern can reduce coupling between components and improve code maintainability and scalability.
Core Concepts
- 1. Publisher/Emitter: Object responsible for emitting events
- 2. Subscriber/Listener: Object that listens and responds to events
- 3. Event: Message emitted by the publisher, usually containing event name and related data
- 4. Event Bus: Middleware responsible for managing subscription relationships and distributing events
This pattern is widely used in the JavaScript ecosystem, such as DOM events, Node.js EventEmitter, Vue's event system, etc.
Basic EventEmitter Implementation
Below is a simple EventEmitter implementation containing the most basic subscribe (on/subscribe) and publish (emit) functionality:
class EventEmitter {
constructor() {
// Store events and corresponding listeners
this.events = {};
}
// Subscribe to events
on(event, listener) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(listener);
return this; // Support chaining
}
// Emit events
emit(event, ...args) {
const listeners = this.events[event];
if (!listeners || listeners.length === 0) {
return false;
}
listeners.forEach(listener => {
listener.apply(this, args);
});
return true;
}
}
// Usage example
const emitter = new EventEmitter();
// Subscribe to events
emitter.on('userLoggedIn', user => {
console.log(`User ${user.name} has logged in`);
});
emitter.on('userLoggedIn', user => {
updateUIForLoggedInUser(user);
});
// Emit events
emitter.emit('userLoggedIn', { name: 'John', id: 123 });
// Output: User John has logged in
Practical Application Scenarios
Below are two simple application examples of the subscribe and emit pattern:
1. Simple Notification System
// Create event manager
const notifier = new EventEmitter();
// Subscribe to different types of notifications
notifier.on('message', data => {
console.log(`Received message: ${data.text}`);
showNotification(data.text);
});
notifier.on('error', data => {
console.error(`Error: ${data.message}`);
showErrorAlert(data.message);
});
// Send notifications in the application
function sendMessage(text) {
notifier.emit('message', { text, time: new Date() });
}
function reportError(message) {
notifier.emit('error', { message, code: 500 });
}
// Usage example
sendMessage('Welcome to the system');
reportError('Connection failed');
2. Simple State Management
// Simple state manager
const store = new EventEmitter();
const state = { count: 0, user: null };
// Subscribe to state changes
store.on('stateChange', (newState) => {
console.log('State updated:', newState);
renderUI(newState);
});
// Method to update state
function updateState(changes) {
Object.assign(state, changes);
store.emit('stateChange', state);
}
// Usage example
function increment() {
updateState({ count: state.count + 1 });
}
function login(user) {
updateState({ user });
}
// Trigger state changes
increment(); // State updated: { count: 1, user: null }
login({ name: 'Alice' }); // State updated: { count: 1, user: { name: 'Alice' } }