Skip to content

Commit

Permalink
Merge pull request #58 from Automattic/refactor/events
Browse files Browse the repository at this point in the history
refactor: events module
  • Loading branch information
sjinks authored Apr 20, 2024
2 parents a6fa909 + f9e186c commit ab3f110
Showing 1 changed file with 28 additions and 69 deletions.
97 changes: 28 additions & 69 deletions lib/events.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
'use strict';

// Modules.
const _ = require('lodash');
const EventEmitter = require('events').EventEmitter;
const {EventEmitter} = require('events');
const Log = require('./logger');
const Promise = require('./promise');

/*
/**
* @typedef {Object} Listener
* @property {string} name The name of the event
* @property {number} priority The priority the event should run in.
* @property {Function} fn The function to call. Should get the args specified in the corresponding `emit` declaration.
*/

/**
* Creates a new Events instance.
*
* @property {Log} log The log instance.
* @property {Listener[]} _listeners The listeners for this event emitter.
*/
class AsyncEvents extends EventEmitter {
constructor(log = new Log()) {
Expand All @@ -25,10 +34,10 @@ class AsyncEvents extends EventEmitter {
*
* @since 3.0.0
* @alias lando.events.on
* @param {String} name The name of the event
* @param {Integer} [priority=5] The priority the event should run in.
* @param {Function} fn The function to call. Should get the args specified in the corresponding `emit` declaration.
* @return {Promise} A Promise
* @param {string} name The name of the event
* @param {number|Function} [priority] If a number, the priority the event should run in; if a function, the function to call.
* @param {Function} [fn] The function to call. Should get the args specified in the corresponding `emit` declaration.
* @return {this}
* @example
* // Print out all our apps as they get instantiated and do it before other `post-instantiate-app` events
* lando.events.on('post-instantiate-app', 1, app => {
Expand All @@ -43,17 +52,16 @@ class AsyncEvents extends EventEmitter {
*/
on(name, priority, fn) {
// Handle no priority
// @todo: is there a way to get this working nicely via es6?
if (_.isUndefined(fn) && _.isFunction(priority)) {
if (typeof fn === 'undefined' && typeof priority === 'function') {
fn = priority;
priority = 5;
}
// Store
this._listeners.push({name, priority, fn});
// Log
this.log.silly('loading event %s priority %s', name, priority);
// Call originl on method.
return this.__on(name, fn);
// Call original on method.
return super.on(name, fn);
};

/**
Expand All @@ -63,58 +71,32 @@ class AsyncEvents extends EventEmitter {
*
* @since 3.0.0
* @alias lando.events.emit
* @param {String} name The name of the event
* @param {...Any} [args] Options args to pass.
* @return {Promise} A Promise
* @param {string} name The name of the event
* @param {...any} args Options args to pass.
* @return {Promise<boolean>} A Promise
* @example
* // Emits a global event with a config arg
* return lando.events.emit('wolf359', config);
*
* // Emits an app event with a config arg
* return app.events.emit('sector001', config);
*/
emit(...args) {
/*
* Helper function that will always return a promise even if function is
* synchronous and doesn't return a promise.
* @todo: this is very old kbox code, can we update it a bit?
*/
const handle = (...args) => {
const fn = args.shift();
const result = fn.apply(this, args);
return Promise.resolve(result);
};
// Save for later
const self = this;
// Grab name of event from first argument.
const name = args.shift();

// Grab priority sorted listeners for this event
const evnts = _(this._listeners)
// Filter by name
emit(name, ...args) {
const fns = this._listeners
.filter(listener => listener.name === name)
// Sort by priority
.sortBy('priority')
// Return value
.value();

// Map to array of func
const fns = _.map(evnts, evnt => evnt.fn);
.sort((a, b) => a.priority - b.priority)
.map(evnt => evnt.fn);

// Log non engine events so we can keep things quiet
if (!_.includes(name, '-engine-')) {
if (!name.includes('-engine-')) {
this.log.debug('emitting event %s', name);
this.log.silly('event %s has %s listeners', name, fns.length);
}

// Make listener functions to a promise in series.
return Promise.each(fns, fn => {
// Clone function arguments.
const fnArgs = args.slice();
// Add listener function to front of arguments.
fnArgs.unshift(fn);
// Apply function that calls the listener function and returns a promise.
return handle.apply(self, fnArgs);
return fn(...args);
})

// Make sure to wait for all mappings.
Expand All @@ -125,30 +107,7 @@ class AsyncEvents extends EventEmitter {
};
};

/*
* Stores the original event on method.
*
* I don't think you want to ever really use this. Mentioned only for transparency.
*
* @alias lando.events.__on
* @see https://nodejs.org/api/events.html
*/
AsyncEvents.prototype.__on = EventEmitter.prototype.on;

/*
* Stores the original event emit method.
*
* I don't think you want to ever really use this. Mentioned only for transparency.
*
* @alias lando.events.__emit
* @see https://nodejs.org/api/events.html
*/
AsyncEvents.prototype.__emit = EventEmitter.prototype.emit;

// Set our maxListeners to something more reasonable for lando
AsyncEvents.prototype.setMaxListeners(64);

/*
* Return the class
*/
module.exports = AsyncEvents;

0 comments on commit ab3f110

Please sign in to comment.