Creating a custom "event listener" with es6 modules

Viewed 477

We are working on creating custom event listener on Javascript. Tried out a few examples but couldn't get custom event listener to be called. Code example:

events.js

function init() {
    return new Promise(() => {
        console.log('successfully initialized!');
    });
}

// this symbolizes some event occurring randomly. in reality, this will eventually be
// something like a webhook getting hit, or a websocket, etc. for now we just use
// setTimeout to simulate this event coming in at some time after init has been called
setTimeout(on, 15000);

// on is called anytime any event occurs. in this example, we hardcode that event1 occurred
function on() {
    console.log('event1 occurred in events.js');
    return 'event1';
}​

const events = {
    init,
    on,
};

export default events;

index.js

events.init(() => {
        // here we should see console log about successful initialization
    }).then(() => {
        // here, we want to "listen" for event1 to occur
        events.on('event1', () => {
            console.log('we were notified about event1 happening!');
        });
    });

In this example we see following being displayed in browser console

successfully initialized!

And then few seconds later:

event1 occurred in events.js

As shown in example callback code for "event1" from index.js is never called, instead "on" function from events.js is called. What we need is a custom event listener to work so we can call "event1" from index.js console.log should display

we were notified about event1 happening!

Thank you for reviewing the question and helping with solution.

1 Answers

I'd say that there are few problems here.

1. Unresolved promise

First of all, you're not resolving the promise, this means that the then callback never gets called.

So, at first you should update the init method as the following:

function init() {
  return new Promise((resolve) => {
    console.log('successfully initialized!');

    // when you're done with your stuff, tell outside
    resolve()
  });
}

2. Tracking callbacks

Than, you're not keeping track of the different callback for the different type of events. The on method should than save the tuple event-callback in an appropriate map as the following:

const handlers = {}
function on(event, callback) {
  handlers[event] = callback;
}

3. Event emission

The last thing that I think you are missing is the concept of "emission" of an event. When something in particular happen, you should emit a proper event. Than, if it has been registered a callback, you're going to call it.

So, you need to add a third emit method to your events api as the following:

function emit(event) {
  const callback = handlers[event];

  // if we registered a callback before, and it is a function,
  // let's call it  
  if ( callback && typeof callback === 'function' )
    callback()
}

Your final export becomes:

const events = {
  init,
  on,
  emit
};

With this new method, you can simulate your call emitting the event event1 as the one you're registering:

setTimeout(() => emit('event1'), 5000);

It seems to work like a charm :)

EDIT

I'm not sure if this might be the exact use case asked by @Shehzad Nizamani, but we can do emit triggers all registered callbacks if no particular events are given, something like:

setTimeout(emit, 5000)

function emit(event) {
  if ( event ) {
    const callback = handlers[event];

    // if we registered a callback before, and it is a function,
    // let's call it  
    if ( callback && typeof callback === 'function' )
      callback()

  // if no event is given, we trigger all the callbacks
  } else {

    const callbacks = Object.values(handlers)
    callbacks.forEach(callback => {
      if ( typeof callback === 'function' )
        callback()
    })
  }
}

function init() {
  return new Promise((resolve) => {
    console.log('successfully initialized!');

    // when you're done with your stuff, tell outside
    resolve()
  });
}

// emit a specific event to trigger the relative callback
setTimeout(() => emit('event1'), 10000);
setTimeout(emit, 5000);

// we need to keep track of a map
// between event types and their callback
const handlers = {}
function on(event, callback) {
  handlers[event] = callback;
}

// we need a new api that triggers registered callbacks
function emit(event) {
  if ( event ) {
    const callback = handlers[event];

    // if we registered a callback before, and it is a function,
    // let's call it  
    if ( callback && typeof callback === 'function' )
      callback()

  // if no event is given, we trigger all the callbacks
  } else {

    const callbacks = Object.values(handlers)
    callbacks.forEach(callback => {
      if ( typeof callback === 'function' )
        callback()
    })
  }
}

const events = {
  init,
  on,
  emit
};

events.init(
  () => {
    // here we should see console log
    //about successful initialization
  }
).then(
  () => {
    
    // here, we want to "listen" for event1 to occur
    events.on('event1', () => {
      console.log('we were notified about event1 happening!');
    });
  }
);

Related