Deep Clone Object
Create a fully independent copy of nested objects where modifying the clone never affects the…
A pub-sub system for decoupled component communication with auto-unsubscribe and error isolation.
class EventBus {
constructor() {
this.listeners = new Map();
}
on(event, callback) {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event).push(callback);
// Return unsubscribe function
return () => {
const callbacks = this.listeners.get(event);
const index = callbacks.indexOf(callback);
if (index > -1) callbacks.splice(index, 1);
};
}
emit(event, data) {
const callbacks = this.listeners.get(event) || [];
for (const callback of callbacks) {
try {
callback(data);
} catch (err) {
console.error(`EventBus: listener error on "${event}"`, err);
}
}
}
once(event, callback) {
const unsubscribe = this.on(event, (data) => {
unsubscribe();
callback(data);
});
return unsubscribe;
}
}
Let us trace what happens when two components communicate through the event bus:
Setup:
CartUI.on("item:added") → listeners Map: { "item:added": [CartUI.update] }
Analytics.on("item:added") → listeners Map: { "item:added": [CartUI.update, Analytics.track] }
Notification.once("item:added") → listeners Map: { "item:added": [CartUI.update, Analytics.track, Notification.show] }
First emit("item:added", { id: 42, name: "Widget" }):
→ CartUI.update({ id: 42, name: "Widget" }) ✓ stays subscribed
→ Analytics.track({ id: 42, name: "Widget" }) ✓ stays subscribed
→ Notification.show({ id: 42, name: "Widget" }) ✓ auto-unsubscribed (once)
Second emit("item:added", { id: 99, name: "Gadget" }):
→ CartUI.update({ id: 99, name: "Gadget" }) ✓ still listening
→ Analytics.track({ id: 99, name: "Gadget" }) ✓ still listening
→ Notification: not called (already removed)
Three design choices make this reliable. First, the try/catch around each callback prevents one broken listener from killing all others. Second, on() returns an unsubscribe function, so cleanup is a simple call rather than manual bookkeeping. Third, once() builds on on() by auto-removing the listener after one call, which prevents memory leaks from forgotten one-time subscriptions.
Without event bus (tight coupling):
ProductPage → calls CartUI.update()
ProductPage → calls Analytics.track()
ProductPage → calls Notification.show()
ProductPage knows about EVERY consumer — hard to add new ones
With event bus (loose coupling):
ProductPage → emits "item:added"
CartUI, Analytics, Notification subscribe independently
ProductPage knows about NOBODY — add new consumers freely
const bus = new EventBus();
// Component A: user authentication
function onLoginSuccess(user) {
bus.emit('auth:login', { userId: user.id, role: user.role });
}
// Component B: update navigation (stays subscribed)
bus.on('auth:login', ({ userId, role }) => {
renderNav(role === 'admin' ? adminMenu : userMenu);
});
// Component C: one-time welcome message
bus.once('auth:login', ({ userId }) => {
showWelcomeBanner(userId);
});
// Component D: cleanup on unmount
const unsubscribe = bus.on('auth:login', syncUserPreferences);
// Later, when component is destroyed:
unsubscribe();
Use an event bus when components need to communicate without knowing about each other. It is ideal for cross-cutting concerns like authentication state, shopping cart updates, and notification systems. For small applications, a single global bus works well. For larger systems, consider scoped buses per feature area to keep event names from colliding.