Publish/Subscribe model in JavaScript

Paweł Ćwik

Let’s figure out how we can use Observer pattern to build a very basic implementation of Events with public/subscribe methods based on usage of Ionic Events.

Introduction

An Observer is a very useful design pattern. We can use it to send information to interested subjects. Let’s figure out how we can build a very basic implementation of Events with public/subscribe methods based on usage of Ionic Events.

Event class

First, we need to create an Event class which will be a container for all methods. We need an array of listeners to have all listeners inside instance. We will write it down as singleton.

Class Events {
    constructor() {
        this.listeners = [];
    }
}

export default new Events();

Subscribe

To make our events mechanism works – first we need to subscribe to an event. Our function will accept two arguments: name of event written as a string and function, which will accept all passed arguments by publish function. We need to check if the same subscriber is not already in listeners array. If passed subscriber isn’t in listeners we can add it to our array.

subscribe(name, fn) {
    const alreadyAdded = this.listeners.some(l => l.name === name && l.fn === fn);

    if (!alreadyAdded) {
        this.listeners.push({ name, fn });
    }
}

Publish

Once we have written subscribe function – we can take care of publish function. This function has to filter existing listeners to find listeners by name and for each of them invoke function with passed arguments.

publish(name, ...args) {
    this.listeners
        .filter(l => l.name === name)
        .forEach(l => l.fn(...args))
}

Unsubscribe

Last function we need to implement is unsubscribe function. We need this function to remove unnecessary listener from our listeners collection.

unsubscribe(name, fn) {
    const index = this.listeners.findIndex(l => l.name === name && l.fn === fn);

    if (index !== -1) {
        this.listeners.splice(index, 1);
    }
}

Usage

This is our Events.js file:

Class Events {

    constructor() {
        this.listeners = [];
    }

    subscribe(name, fn) {
        const alreadyAdded = this.listeners.some(l => l.name === name && l.fn === fn);
 
        if (!alreadyAdded) {
            this.listeners.push({ name, fn });
        }
    }

     publish(name, ...args) {
        this.listeners
            .filter(l => l.name === name)
            .forEach(l => l.fn(...args))
     }

     unsubscribe(name, fn) {
        const index = this.listeners.findIndex(l => l.name === name && l.fn === fn);
 
        if (index !== -1) {
            this.listeners.splice(index, 1);
        }
    }
}

export default new Events();

Now we have to check if everything works as designed. We need to create two subscribers and subscribe to action.

import Events from './events';

Events.subscribe('button.click', (...args) => {
    console.log('Subscribing click event on Subscriber 1: ', args);
});

Now we have to test if subscriber works. We can do that in this way:

import Events from './events';

// Importing Subscribers
import './subscriber1.js';
import './subscriber2.js';

const button = document.querySelector('.button');
button.addEventListener('click', () => {
   Events.publish('button.click', 1, 2, 3);
});

Summary

Now you should understand how publish/subscribe model works under the hood. This is very basic implementation, but works. You can find this examples and code on my GitHub

Ionic Framework

Repository

Poznaj mageek of j‑labs i daj się zadziwić, jak może wyglądać praca z j‑People!

Skontaktuj się z nami