UNPKG

3.78 kBJavaScriptView Raw
1/*!
2 * Copyright (c) 2012-2019 Digital Bazaar, Inc. All rights reserved.
3 */
4'use strict';
5
6const events = {
7 EventEmitter: require('async-node-events')
8};
9
10const api = new events.EventEmitter();
11api.setMaxListeners(0);
12api.removeAllListeners('maxListenersPassed');
13module.exports = api;
14
15// store original emit function
16const emit = api.emit;
17
18/**
19 * Emits an event just like the standard EventEmitter does, however, if the
20 * last parameter passed is a function, it is assumed to be a callback that
21 * will be called once the event has propagated to all listeners or it has
22 * been canceled due to an error.
23 *
24 * Note that this module also permits listeners to be executed asynchronously.
25 * Any asynchronous listeners will be notified of an event in order of
26 * registration. Each listener will block the next listener to receive the
27 * event until the callback that is passed to it is called. If an error is
28 * passed as the first parameter to the callback, then the event will be
29 * canceled.
30 *
31 * Backwards-compatibility support exists for the deprecated method of passing
32 * the first argument as an object with a 'type' property that indicates the
33 * event type.
34 *
35 * So two forms are available:
36 * * The standard emit() usage of an event type and an arbitrary number of
37 * arguments.
38 * * DEPRECATED: A single event object argument with the following fields:
39 * type: event type (string)
40 * time: event time (Date, optional, added if omitted)
41 * details: event details (object)
42 *
43 * @return a Promise that resolves to undefined once the event has been emitted
44 * to all listeners (or to `false` if it was canceled).
45 */
46api.emit = function(event /* ... */) {
47 let args;
48 // check for event object
49 if(typeof event === 'object' && event.type) {
50 // add time if not present
51 event.time = event.time || new Date();
52 args = [event.type, event];
53 } else {
54 // otherwise assume regular emit call
55 args = Array.prototype.slice.call(arguments);
56 }
57
58 // default callback
59 let callback = () => {};
60 // the second conditional here is designed to differentiate functions like
61 // bedrock-express `server` from an actual callback function
62 if(typeof args[args.length - 1] === 'function' &&
63 Object.keys(args[args.length - 1]).length === 0) {
64 // save `callback` to call once `emit` completes
65 callback = args[args.length - 1];
66 args.pop();
67 }
68
69 // call callback that resolves promise
70 const deferred = {};
71 const promise = new Promise((resolve, reject) => {
72 deferred.resolve = resolve;
73 deferred.reject = reject;
74 });
75 args.push((err, result) => {
76 if(err) {
77 deferred.reject(err);
78 } else {
79 deferred.resolve(result);
80 }
81 callback(err, result);
82 });
83
84 // emit
85 emit.apply(api, args);
86 return promise;
87};
88
89/**
90 * Schedules an event to be emitted on the next tick (via process.nextTick).
91 *
92 * Backwards-compatibility support exists for the deprecated method of passing
93 * the first argument as an object with a 'type' property that indicates the
94 * event type.
95 *
96 * So two forms are available:
97 * * The standard emit() usage of an event type and an arbitrary number of
98 * arguments.
99 * * DEPRECATED: A single event object argument with the following fields:
100 * type: event type (string)
101 * time: event time (Date, optional, added if omitted)
102 * details: event details (object)
103 *
104 * @return a Promise that resolves to undefined once the event has been emitted
105 * to all listeners (or to `false` if it was canceled).
106 */
107api.emitLater = function(/* ... */) {
108 // emit asynchronously
109 const args = arguments;
110 return new Promise((resolve, reject) => {
111 process.nextTick(() => {
112 api.emit.apply(api, args).then(resolve, reject);
113 });
114 });
115};