|
23 | 23 |
|
24 | 24 | const { |
25 | 25 | Array, |
| 26 | + Boolean, |
26 | 27 | Error, |
27 | 28 | MathMin, |
28 | 29 | NumberIsNaN, |
@@ -33,7 +34,10 @@ const { |
33 | 34 | Promise, |
34 | 35 | ReflectApply, |
35 | 36 | ReflectOwnKeys, |
| 37 | + Symbol, |
| 38 | + SymbolFor, |
36 | 39 | } = primordials; |
| 40 | +const kRejection = SymbolFor('nodejs.rejection'); |
37 | 41 |
|
38 | 42 | let spliceOne; |
39 | 43 |
|
@@ -51,8 +55,10 @@ const { |
51 | 55 | inspect |
52 | 56 | } = require('internal/util/inspect'); |
53 | 57 |
|
54 | | -function EventEmitter() { |
55 | | - EventEmitter.init.call(this); |
| 58 | +const kCapture = Symbol('kCapture'); |
| 59 | + |
| 60 | +function EventEmitter(opts) { |
| 61 | + EventEmitter.init.call(this, opts); |
56 | 62 | } |
57 | 63 | module.exports = EventEmitter; |
58 | 64 | module.exports.once = once; |
@@ -62,6 +68,29 @@ EventEmitter.EventEmitter = EventEmitter; |
62 | 68 |
|
63 | 69 | EventEmitter.usingDomains = false; |
64 | 70 |
|
| 71 | +EventEmitter.captureRejectionSymbol = kRejection; |
| 72 | +ObjectDefineProperty(EventEmitter, 'captureRejections', { |
| 73 | + get() { |
| 74 | + return EventEmitter.prototype[kCapture]; |
| 75 | + }, |
| 76 | + set(value) { |
| 77 | + if (typeof value !== 'boolean') { |
| 78 | + throw new ERR_INVALID_ARG_TYPE('EventEmitter.captureRejections', |
| 79 | + 'boolean', value); |
| 80 | + } |
| 81 | + |
| 82 | + EventEmitter.prototype[kCapture] = value; |
| 83 | + }, |
| 84 | + enumerable: true |
| 85 | +}); |
| 86 | + |
| 87 | +// The default for captureRejections is false |
| 88 | +ObjectDefineProperty(EventEmitter.prototype, kCapture, { |
| 89 | + value: false, |
| 90 | + writable: true, |
| 91 | + enumerable: false |
| 92 | +}); |
| 93 | + |
65 | 94 | EventEmitter.prototype._events = undefined; |
66 | 95 | EventEmitter.prototype._eventsCount = 0; |
67 | 96 | EventEmitter.prototype._maxListeners = undefined; |
@@ -91,7 +120,7 @@ ObjectDefineProperty(EventEmitter, 'defaultMaxListeners', { |
91 | 120 | } |
92 | 121 | }); |
93 | 122 |
|
94 | | -EventEmitter.init = function() { |
| 123 | +EventEmitter.init = function(opts) { |
95 | 124 |
|
96 | 125 | if (this._events === undefined || |
97 | 126 | this._events === ObjectGetPrototypeOf(this)._events) { |
@@ -100,8 +129,64 @@ EventEmitter.init = function() { |
100 | 129 | } |
101 | 130 |
|
102 | 131 | this._maxListeners = this._maxListeners || undefined; |
| 132 | + |
| 133 | + |
| 134 | + if (opts && opts.captureRejections) { |
| 135 | + if (typeof opts.captureRejections !== 'boolean') { |
| 136 | + throw new ERR_INVALID_ARG_TYPE('options.captureRejections', |
| 137 | + 'boolean', opts.captureRejections); |
| 138 | + } |
| 139 | + this[kCapture] = Boolean(opts.captureRejections); |
| 140 | + } else { |
| 141 | + // Assigning it directly a prototype lookup, as it slighly expensive |
| 142 | + // and it sits in a very sensitive hot path. |
| 143 | + this[kCapture] = EventEmitter.prototype[kCapture]; |
| 144 | + } |
103 | 145 | }; |
104 | 146 |
|
| 147 | +function addCatch(that, promise, type, args) { |
| 148 | + if (!that[kCapture]) { |
| 149 | + return; |
| 150 | + } |
| 151 | + |
| 152 | + // Handle Promises/A+ spec, then could be a getter |
| 153 | + // that throws on second use. |
| 154 | + try { |
| 155 | + const then = promise.then; |
| 156 | + |
| 157 | + if (typeof then === 'function') { |
| 158 | + then.call(promise, undefined, function(err) { |
| 159 | + // The callback is called with nextTick to avoid a follow-up |
| 160 | + // rejection from this promise. |
| 161 | + process.nextTick(emitUnhandledRejectionOrErr, that, err, type, args); |
| 162 | + }); |
| 163 | + } |
| 164 | + } catch (err) { |
| 165 | + that.emit('error', err); |
| 166 | + } |
| 167 | +} |
| 168 | + |
| 169 | +function emitUnhandledRejectionOrErr(ee, err, type, args) { |
| 170 | + if (typeof ee[kRejection] === 'function') { |
| 171 | + ee[kRejection](err, type, ...args); |
| 172 | + } else { |
| 173 | + // We have to disable the capture rejections mechanism, otherwise |
| 174 | + // we might end up in an infinite loop. |
| 175 | + const prev = ee[kCapture]; |
| 176 | + |
| 177 | + // If the error handler throws, it is not catcheable and it |
| 178 | + // will end up in 'uncaughtException'. We restore the previous |
| 179 | + // value of kCapture in case the uncaughtException is present |
| 180 | + // and the exception is handled. |
| 181 | + try { |
| 182 | + ee[kCapture] = false; |
| 183 | + ee.emit('error', err); |
| 184 | + } finally { |
| 185 | + ee[kCapture] = prev; |
| 186 | + } |
| 187 | + } |
| 188 | +} |
| 189 | + |
105 | 190 | // Obviously not all Emitters should be limited to 10. This function allows |
106 | 191 | // that to be increased. Set to zero for unlimited. |
107 | 192 | EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { |
@@ -218,12 +303,29 @@ EventEmitter.prototype.emit = function emit(type, ...args) { |
218 | 303 | return false; |
219 | 304 |
|
220 | 305 | if (typeof handler === 'function') { |
221 | | - ReflectApply(handler, this, args); |
| 306 | + const result = ReflectApply(handler, this, args); |
| 307 | + |
| 308 | + // We check if result is undefined first because that |
| 309 | + // is the most common case so we do not pay any perf |
| 310 | + // penalty |
| 311 | + if (result !== undefined && result !== null) { |
| 312 | + addCatch(this, result, type, args); |
| 313 | + } |
222 | 314 | } else { |
223 | 315 | const len = handler.length; |
224 | 316 | const listeners = arrayClone(handler, len); |
225 | | - for (let i = 0; i < len; ++i) |
226 | | - ReflectApply(listeners[i], this, args); |
| 317 | + for (var i = 0; i < len; ++i) { |
| 318 | + const result = ReflectApply(listeners[i], this, args); |
| 319 | + |
| 320 | + // We check if result is undefined first because that |
| 321 | + // is the most common case so we do not pay any perf |
| 322 | + // penalty. |
| 323 | + // This code is duplicated because extracting it away |
| 324 | + // would make it non-inlineable. |
| 325 | + if (result !== undefined && result !== null) { |
| 326 | + addCatch(this, result, type, args); |
| 327 | + } |
| 328 | + } |
227 | 329 | } |
228 | 330 |
|
229 | 331 | return true; |
|