sails
Version:
API-driven framework for building realtime apps, using MVC conventions (based on Express and Socket.io)
117 lines (91 loc) • 3.63 kB
JavaScript
/**
* Module dependencies
*/
var _ = require('@sailshq/lodash');
var async = require('async');
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// FUTURE
// Pull _most of this_ into a separate module, since it's not specific
// to Sails, and has come up in a few different places.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
* Mix-in an `after` function to an EventEmitter.
*
* If `events` have already fired, trigger fn immediately (with no args)
* Otherwise bind a normal one-time event using `EventEmitter.prototype.once()`.
* Useful for checking whether or not something has finished loading, etc.
*
* This is a lot like jQuery's `$(document).ready()`.
*
* @param {EventEmitter} emitter The Sails application instance
*/
module.exports = function mixinAfter(emitter) {
/**
* { emitter.warmEvents }
*
* Events which have occurred at least once
* (Required to support `emitter.after()`)
*/
emitter.warmEvents = {};
var _originalEmit = emitter.emit;
/**
* emitter.emit()
*
* Synchronously calls each of the listeners registered for the event named eventName, in the order they were registered, passing the supplied arguments to each.
*
* Override `EventEmitter.prototype.emit` to keep track of all the events that have occurred once.
* (Required to support `emitter.after()`)
*
* @param {String} eventName [name of the event]
* @return {Boolean} Returns true if the event had listeners, false otherwise.
* @see https://nodejs.org/api/events.html#events_emitter_emit_eventname_args
*/
emitter.emit = function(eventName) {
var args = Array.prototype.slice.call(arguments, 0);
emitter.warmEvents[eventName] = true;
return _originalEmit.apply(emitter, args);
};
/**
* `emitter.after()`
*
* Fires your handler **IF THE SPECIFIED EVENT HAS ALREADY BEEN TRIGGERED** or **WHEN IT IS TRIGGERED**.
*
* @param {String|Array} events [name of the event(s)]
* @param {Function} fn [event handler function]
*/
emitter.after = function(events, fn) {
// Support a single event or an array of events
if (!_.isArray(events)) {
events = [events];
}
// Convert named event dependencies into an array
// of async-compatible functions.
var dependencies = _.reduce(events, function (dependencies, event) {
// Push on the handler function.
dependencies.push(function handlerFn(cb) {
// If the event has already fired, then just execute our callback.
if (emitter.warmEvents[event]) {
return cb();
}
// But otherwise, bind a one-time-use handler that listens for the
// first time this event is fired, and then executes our callback
// once it does.
else {
emitter.once(event, function (){
return cb();
});
}
});//</declared and pushed on handler function>
return dependencies;
}, []);//</_.reduce() :: iterate over each event in order to build `dependencies` (an array of handler functions)>
// When all events have fired, call `fn`
// (all arguments passed to `emit()` calls are discarded)
async.parallel(dependencies, function(err) {
if (err) {
console.error('Consistency violation: Received `err`, but this should be impossible! Here is the error: '+err.stack);
console.error('^^^If you are seeing this message, then please report this error at http://sailsjs.com/bugs. (Continuing anyway...)');
}//>-
return fn();
});
};
};