1 | /*!
|
2 | * Copyright (c) 2012-2019 Digital Bazaar, Inc. All rights reserved.
|
3 | */
|
4 | ;
|
5 |
|
6 | const events = {
|
7 | EventEmitter: require('async-node-events')
|
8 | };
|
9 |
|
10 | const api = new events.EventEmitter();
|
11 | api.setMaxListeners(0);
|
12 | api.removeAllListeners('maxListenersPassed');
|
13 | module.exports = api;
|
14 |
|
15 | // store original emit function
|
16 | const 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 | */
|
46 | api.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 | */
|
107 | api.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 | };
|