Почему этот одноэлементный интерфейс EventEmitter pubsub не работает node.js ?

#node.js #singleton #publish-subscribe #eventemitter

#node.js #одноэлементный #опубликовать-подписаться #eventemitter

Вопрос:

Я пытаюсь разделить издателя и подписчика в node.js чтобы иметь возможность отправлять данные друг другу через общий экземпляр EventEmitter в качестве шины.

Моя шина следует одноэлементному методу, обсуждаемому [ЗДЕСЬ] [1]

bus.js файл

 // https://derickbailey.com/2016/03/09/creating-a-true-singleton-in-node-js-with-es6-symbols/
// create a unique, global symbol name
// -----------------------------------

const FOO_KEY = Symbol.for("test.exchanges.bus");
const EventEmitter = require("events");

// check if the global object has this symbol
// add it if it does not have the symbol, yet
// ------------------------------------------

var globalSymbols = Object.getOwnPropertySymbols(global);
var hasFoo = (globalSymbols.indexOf(FOO_KEY) > -1);

if (!hasFoo){
  global[FOO_KEY] = {
    foo: new EventEmitter()
  };
}

// define the singleton API
// ------------------------

var singleton = {};

Object.defineProperty(singleton, "instance", {
  get: function(){
    return global[FOO_KEY];
  }
});

// ensure the API is never changed
// -------------------------------

Object.freeze(singleton);

// export the singleton API only
// -----------------------------

module.exports = singleton;
  

Насколько я понимаю, когда мне требуется этот файл в разных модулях, должен быть доступен один и тот же объект foo. Разве это не цель наличия синглтона?

pub.js файл

 const bus = require("./bus");

class Publisher {
    constructor(emitter) {
        this.emitter = emitter;
        console.log(this.emitter);
        this.test();
    }

    test() {
        setInterval(() => {
            this.emitter.emit("test", Date.now());
        }, 1000);
    }

}

module.exports = Publisher;

console.log(bus.instance.foo);
  

sub.js файл

 const bus = require("./bus");

class Subscriber {
    constructor(emitter) {
        this.emitter = emitter;
        console.log(this.emitter);

        this.emitter.on("test", this.handleTest);
    }

    handleTest(data) {
        console.log("handling test", data);
    }
}

module.exports = Subscriber;

console.log(bus.instance.foo);
  

Когда я запускаю pub.js и sub.js в 2 отдельных окнах терминала, sub.js завершается выполнение немедленно, как если бы издатель не отправлял ему сообщения. Не мог бы кто-нибудь любезно указать, как разделить издателя и подписчика для работы с одной и той же шиной событий?

Ответ №1:

Вы можете рассмотреть возможность перепроектирования вашего модуля шины. Я рекомендую создать его как класс, который расширяется EventEmitter , а затем возвращать созданный экземпляр этого класса.

Теперь, когда этот файл загружается в первый раз, код класса будет запущен, и объект будет создан, а затем экспортирован обратно. require этот экземпляр будет кэшироваться, и в следующий раз, когда этот файл будет загружен, он получит тот же объект обратно. это делает его одноэлементным, и теперь вы можете использовать его как общую шину.

вот некоторый код, демонстрирующий этот момент:

bus.js

 const {EventEmitter} = require('events');
class Bus extends EventEmitter {
  constructor(){
    super();
    setTimeout(() => this.emit('foo', (new Date()).getTime()), 1000);
  }
}
module.exports = new Bus();
  

bus.spec.js

 const bus1 = require('../src/util/bus');
const bus2 = require('../src/util/bus');

describe('bus', () => {
  it('is the same object', () => {
    expect(bus1).toEqual(bus2);
    expect(bus1 === bus2).toEqual(true);
  });
  
  it('calls the event handler on bus2 when event is emitted on bus1', done => {
    bus2.on('chocolate', flavor => {
      expect(flavor).toBe('dark');
      done()
    });
    bus1.emit('chocolate', 'dark');
  }, 20)
});