preface

The difference between publish subscribe and observer patterns has been written before, but the implementation was sloppy. Today it’s more detailed to write a publish subscribe implementation. Because, you know, the post and subscribe guy is so strong. It’s all over the place. Node inherits a lot of its built-in publish/subscribe module implementations, and when I first wrote Jq they were all shadows of it, such as the trigger and on methods

role

  • A one-to-many relationship between objects. When an object’s state changes, all objects that depend on it are notified of the change
  • To decouple callback events, one object can cache multiple callback events, waiting for uniform subscribe(publish).

implementation

EventHub Object structure

Const eventHub = {events: {}, on: (a) = > {/ / event subscription}, emit: () = > {/ / event publishing}, off: (eventName, callback) => {if (eventName, callback) => {if (eventName, callback) =>},};Copy the code

Event subscription on()

  • Fault tolerance when events objects do not exist
  • EventName does not exist. EventName is used as the key and callback is used as the value array in the Events object
  • Push the current callback in the array with eventName as the key
On: (eventName, callback) => {//events object is not saved, return a {} as this. Events if (! this.events) { this.events = {}; } if (this.events[eventName]) { this.events[eventName].push(callback); } else { this.events[eventName] = [callback]; }}Copy the code

Event emit()

  • Find the events object eventName corresponding to the callback event array traversal & execute in sequence
emit: (eventName, ... args) => { this.events[eventName].forEach((fn) => fn(... args)); },Copy the code
  • To test the effect.
Const cry = () => {console.log(" cry "); }; EventHub. On (" Zoe is brokenhearted ", cry); SetTimeout (() => {eventHub. Emit ("zoe is in love "); }, 1000);Copy the code

Event release off()

  • Function: Stops subscription to a specific callback event corresponding to a specific event
  • Implementation: return a new callback event array by filtering the event using the array filter
off: (eventName, Callback) => {if (this.events && this.events[eventName]) {// Delete the callback function this. Events [eventName] = this.events[eventName].filter( (fn) => fn ! == callback ); }},Copy the code
  • Implementation effect
Const cry = () => {console.log(" cry "); }; EventHub. On (" Zoe is brokenhearted ", cry); const fn = () => { console.log("shopping"); }; EventHub. On (" Zoe is brokenhearted ", fn); SetTimeout () = > {/ / eventHub off (" zoe brokenhearted, fn); EventHub. Emit (" Zoe is in love "); }, 1000);Copy the code

Effect without off:(fn is successfully off) :

Event release once()

  • Based on on and off only give the call once after giving off
once: (eventName, callback) => {// define a one event to do eventName's subscribe callback event const one = () => {// Callback (); eventHub.off(eventName, one); }; eventHub.on(eventName, one); },Copy the code
  • call
Const cry = () => {console.log(" cry "); }; EventHub. On (" Zoe is brokenhearted ", cry); Const fn = () => {console.log("shopping "); }; EventHub. Once (" Zoe is brokenhearted ", fn); SetTimeout (() => {//eventHub. Off (" Zoe is broken in love ", fn); // Potential problem eventHub. Emit (" Zoe is brokenhearted "); EventHub. Emit (" Zoe is in love "); }, 1000);Copy the code
  • Effect: the first time cry and FN are printed, the second time only cry is printed

  • Once listener events are not successfully off

  • To solve

The once method assigns the callback to an attribute l of one

one.l = callback; EventHub. On (eventName, one);Copy the code

Add one for filter in off

(fn) => fn ! == callback && fn.l ! == callbackCopy the code
  • Effect: Off (perfect)

The complete code

const eventHub = { events: {}, on: (eventName, callback) => { if (! this.events) { this.events = {}; } if (this.events[eventName]) { this.events[eventName].push(callback); } else { this.events[eventName] = [callback]; } }, emit: (eventName, ... args) => { this.events[eventName].forEach((fn) => fn(... args)); }, off: (eventName, Callback) => {if (this.events && this.events[eventName]) {// Delete the callback function this. Events [eventName] = this.events[eventName].filter( (fn) => fn ! == callback && fn.l ! == callback ); } }, once: (eventName, callback) => { const one = () => { callback(); eventHub.off(eventName, one); }; one.l = callback; EventHub. On (eventName, one); }};Copy the code

Class is publish-subscribe

  • The object form is implemented primarily to understand the process
  • But in the actual work, it is impossible to put it on global variables, so it is not easy to reuse the problem
  • We’ve seen that The Implementation of EventEmitter in Node is in class, so let’s write it as a class again
  • Code implementation
function EventHubs() { this.events = {}; } EventHubs.prototype.on = function (eventName, callback) { if (! this.event) { this.event = {}; } if (this.event[eventName]) { this.event[eventName].push(callback); } else { this.event[eventName] = [callback]; }}; EventHubs.prototype.emit = function (eventName, ... args) { this.events[eventName].forEach((fn) => fn(... args)); }; EventHubs.prototype.off = function (eventName, callback) { if (this.events && this.events[eventName]) { this.events[eventName] = this.events[eventName].filter( (fn) =>  fn ! == callback && fn.l ! == callback ); }}; EventHubs.prototype.once = function (eventName, callback) { const one = () => { callback(); this.off(eventName, one); }; one.l = callback; this.on(eventName, one); }; module.exports = EventHubs;Copy the code

Finally, if you find this article helpful, please click “Like” three times. Thank you very much