events-ex 2.3.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +1 -1
- package/CHANGELOG.md +25 -0
- package/README.cn.md +114 -3
- package/README.md +106 -5
- package/coverage/all-off.js.html +133 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/clover.xml +572 -0
- package/coverage/consts.js.html +154 -0
- package/coverage/default-methods.js.html +2068 -0
- package/coverage/event-emitter.js.html +121 -0
- package/coverage/event.js.html +286 -0
- package/coverage/eventable.js.html +355 -0
- package/coverage/favicon.png +0 -0
- package/coverage/has-listeners.js.html +166 -0
- package/coverage/index.html +281 -0
- package/coverage/lcov-report/all-off.js.html +133 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/consts.js.html +154 -0
- package/coverage/lcov-report/default-methods.js.html +2068 -0
- package/coverage/lcov-report/event-emitter.js.html +121 -0
- package/coverage/lcov-report/event.js.html +286 -0
- package/coverage/lcov-report/eventable.js.html +355 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/has-listeners.js.html +166 -0
- package/coverage/lcov-report/index.html +281 -0
- package/coverage/lcov-report/once-promise.js.html +325 -0
- package/coverage/lcov-report/pipe-async.js.html +385 -0
- package/coverage/lcov-report/pipe.js.html +244 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov-report/unify.js.html +259 -0
- package/coverage/lcov-report/wrap-event-emitter.js.html +223 -0
- package/coverage/lcov.info +1201 -0
- package/coverage/once-promise.js.html +325 -0
- package/coverage/pipe-async.js.html +385 -0
- package/coverage/pipe.js.html +244 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/coverage/unify.js.html +259 -0
- package/coverage/wrap-event-emitter.js.html +223 -0
- package/docs/README.md +106 -5
- package/docs/all-off/functions/allOff.md +1 -1
- package/docs/consts/README.md +4 -0
- package/docs/consts/functions/createAbortError.md +18 -0
- package/docs/consts/variables/RegExpEventSymbol.md +1 -1
- package/docs/consts/variables/states.md +2 -2
- package/docs/default-methods/functions/getEventableMethods.md +34 -18
- package/docs/event/classes/Event.md +23 -13
- package/docs/event-emitter/classes/EventEmitter-1.md +2 -2
- package/docs/event-emitter/classes/EventEmitter.md +28 -28
- package/docs/eventable/functions/eventable.md +1 -1
- package/docs/has-listeners/functions/hasListeners.md +2 -2
- package/docs/index/README.md +12 -0
- package/docs/modules.md +1 -0
- package/docs/once-promise/README.md +17 -0
- package/docs/once-promise/functions/oncePromise.md +60 -0
- package/docs/pipe/functions/pipe.md +3 -3
- package/docs/pipe-async/functions/pipeAsync.md +3 -3
- package/docs/unify/functions/unify.md +1 -1
- package/docs/util/array-remove/functions/remove.md +1 -1
- package/docs/util/object-for-each/functions/forEach.md +1 -1
- package/docs/util/string-pad/functions/pad.md +1 -1
- package/docs/util/to-int/functions/toInt.md +1 -1
- package/docs/util/valid-callable/functions/validCallable.md +1 -1
- package/docs/util/valid-object/functions/validObject.md +1 -1
- package/docs/wrap-event-emitter/functions/wrapEventEmitter.md +3 -3
- package/docs/wrap-event-emitter/variables/methods.md +1 -1
- package/lib/all-off.mjs +16 -0
- package/lib/consts.d.ts +7 -1
- package/lib/consts.js +17 -2
- package/lib/consts.mjs +23 -0
- package/lib/default-methods.d.ts +12 -4
- package/lib/default-methods.js +174 -22
- package/lib/default-methods.mjs +661 -0
- package/lib/event-emitter.mjs +12 -0
- package/lib/event.d.ts +10 -4
- package/lib/event.js +6 -0
- package/lib/event.mjs +67 -0
- package/lib/eventable.d.ts +1 -1
- package/lib/eventable.mjs +90 -0
- package/lib/has-listeners.d.ts +1 -1
- package/lib/has-listeners.mjs +27 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +12 -0
- package/lib/index.mjs +16 -0
- package/lib/once-promise.d.ts +23 -0
- package/lib/once-promise.js +82 -0
- package/lib/once-promise.mjs +80 -0
- package/lib/pipe-async.d.ts +4 -2
- package/lib/pipe-async.js +16 -3
- package/lib/pipe-async.mjs +100 -0
- package/lib/pipe.d.ts +1 -1
- package/lib/pipe.mjs +53 -0
- package/lib/unify.js +1 -1
- package/lib/unify.mjs +58 -0
- package/lib/util/array-remove.mjs +16 -0
- package/lib/util/object-for-each.mjs +20 -0
- package/lib/util/promise-any.mjs +45 -0
- package/lib/util/string-pad.mjs +19 -0
- package/lib/util/to-int.mjs +9 -0
- package/lib/util/valid-callable.mjs +6 -0
- package/lib/util/valid-object.mjs +8 -0
- package/lib/wrap-event-emitter.d.ts +1 -1
- package/lib/wrap-event-emitter.mjs +46 -0
- package/package.json +29 -20
- package/scripts/copy-to-mjs.mjs +79 -0
- package/src/consts.js +10 -0
- package/src/default-methods.js +169 -24
- package/src/event.js +6 -0
- package/src/index.js +1 -0
- package/src/once-promise.js +80 -0
- package/src/pipe-async.js +16 -3
- package/src/unify.js +1 -1
- package/tsconfig.json +2 -1
- package/vitest.config.js +61 -0
- package/.mocharc.yml +0 -3
package/lib/consts.d.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates an AbortError with name 'AbortError'.
|
|
3
|
+
* Ensures a fresh Error instance each call for proper stack traces.
|
|
4
|
+
* @returns {Error}
|
|
5
|
+
*/
|
|
6
|
+
export function createAbortError(): Error;
|
|
1
7
|
export namespace states {
|
|
2
8
|
export { CONTINUE };
|
|
3
9
|
export { DONE };
|
|
@@ -5,7 +11,7 @@ export namespace states {
|
|
|
5
11
|
export { ABORT };
|
|
6
12
|
}
|
|
7
13
|
export const RegExpEventSymbol: symbol | "@@RegExpEvent";
|
|
8
|
-
declare const CONTINUE:
|
|
14
|
+
declare const CONTINUE: undefined;
|
|
9
15
|
declare const DONE: 1;
|
|
10
16
|
declare const STOPPED: 2;
|
|
11
17
|
declare const ABORT: -1;
|
package/lib/consts.js
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.
|
|
6
|
+
exports.RegExpEventSymbol = void 0;
|
|
7
|
+
exports.createAbortError = createAbortError;
|
|
8
|
+
exports.states = void 0;
|
|
7
9
|
const CONTINUE = undefined;
|
|
8
10
|
const DONE = 1;
|
|
9
11
|
const STOPPED = 2;
|
|
@@ -14,4 +16,17 @@ const states = exports.states = {
|
|
|
14
16
|
STOPPED,
|
|
15
17
|
ABORT
|
|
16
18
|
};
|
|
17
|
-
|
|
19
|
+
|
|
20
|
+
/* v8 ignore next — false branch only reachable without Symbol, which is impossible in Node.js */
|
|
21
|
+
const RegExpEventSymbol = exports.RegExpEventSymbol = typeof Symbol === 'function' ? Symbol('RegExpEvent') : '@@RegExpEvent';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Creates an AbortError with name 'AbortError'.
|
|
25
|
+
* Ensures a fresh Error instance each call for proper stack traces.
|
|
26
|
+
* @returns {Error}
|
|
27
|
+
*/
|
|
28
|
+
function createAbortError() {
|
|
29
|
+
return Object.assign(new Error('The operation was aborted'), {
|
|
30
|
+
name: 'AbortError'
|
|
31
|
+
});
|
|
32
|
+
}
|
package/lib/consts.mjs
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const CONTINUE = undefined
|
|
2
|
+
const DONE = 1
|
|
3
|
+
const STOPPED = 2
|
|
4
|
+
const ABORT = -1
|
|
5
|
+
|
|
6
|
+
export const states = {
|
|
7
|
+
CONTINUE,
|
|
8
|
+
DONE,
|
|
9
|
+
STOPPED,
|
|
10
|
+
ABORT,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/* v8 ignore next — false branch only reachable without Symbol, which is impossible in Node.js */
|
|
14
|
+
export const RegExpEventSymbol = typeof Symbol === 'function' ? Symbol('RegExpEvent') : '@@RegExpEvent'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Creates an AbortError with name 'AbortError'.
|
|
18
|
+
* Ensures a fresh Error instance each call for proper stack traces.
|
|
19
|
+
* @returns {Error}
|
|
20
|
+
*/
|
|
21
|
+
export function createAbortError() {
|
|
22
|
+
return Object.assign(new Error('The operation was aborted'), { name: 'AbortError' })
|
|
23
|
+
}
|
package/lib/default-methods.d.ts
CHANGED
|
@@ -4,11 +4,19 @@ export function getEventableMethods(aClass: any): {
|
|
|
4
4
|
* @param {Object} options - Configuration options for event emission.
|
|
5
5
|
* @param {string} [options.asyncMode='serial'] - The mode of asynchronous emission ('serial' or 'parallel').
|
|
6
6
|
* @param {string} [options.resultMode='last'] - The strategy for handling multiple return values ('last', 'first', 'collect').
|
|
7
|
+
* @param {AbortSignal} [options.signal] - An AbortSignal to cancel async event emission.
|
|
8
|
+
* @param {boolean|null} [options.raiseError] - Controls error handling behavior:
|
|
9
|
+
* - `true`: Always throw listener errors immediately.
|
|
10
|
+
* - `false`: Silently swallow listener errors.
|
|
11
|
+
* - `null`: Throw only for 'error' events with no error listeners (Node.js default).
|
|
12
|
+
* - `undefined` (default): Same as `false` for emitAsync.
|
|
7
13
|
* @returns {import('./event-emitter').EventEmitter} A proxy object representing the configured EventEmitter.
|
|
8
14
|
*/
|
|
9
15
|
configure(options: {
|
|
10
|
-
asyncMode?: string;
|
|
11
|
-
resultMode?: string;
|
|
16
|
+
asyncMode?: string | undefined;
|
|
17
|
+
resultMode?: string | undefined;
|
|
18
|
+
signal?: any;
|
|
19
|
+
raiseError?: boolean | null | undefined;
|
|
12
20
|
}): import("./event-emitter").EventEmitter;
|
|
13
21
|
/**
|
|
14
22
|
* A shortcut for parallel configuration.
|
|
@@ -18,10 +26,10 @@ export function getEventableMethods(aClass: any): {
|
|
|
18
26
|
parallel(resultMode?: string): import("./event-emitter").EventEmitter;
|
|
19
27
|
/**
|
|
20
28
|
* Sets the configuration options for the EventEmitter instance.
|
|
21
|
-
* @param {Object} options - Configuration options for the emitter (e.g., asyncMode, resultMode, maxListeners).
|
|
29
|
+
* @param {Object} options - Configuration options for the emitter (e.g., asyncMode, resultMode, maxListeners, raiseError).
|
|
22
30
|
* @returns {import('./event-emitter').EventEmitter} The EventEmitter instance for chaining.
|
|
23
31
|
*/
|
|
24
|
-
setEmitterOptions(options:
|
|
32
|
+
setEmitterOptions(options: Object): import("./event-emitter").EventEmitter;
|
|
25
33
|
/**
|
|
26
34
|
* Adds a listener function to the specified event type.
|
|
27
35
|
* @param {string|RegExp} type - The event type to listen for.
|
package/lib/default-methods.js
CHANGED
|
@@ -23,6 +23,12 @@ function getEventableMethods(aClass) {
|
|
|
23
23
|
* @param {Object} options - Configuration options for event emission.
|
|
24
24
|
* @param {string} [options.asyncMode='serial'] - The mode of asynchronous emission ('serial' or 'parallel').
|
|
25
25
|
* @param {string} [options.resultMode='last'] - The strategy for handling multiple return values ('last', 'first', 'collect').
|
|
26
|
+
* @param {AbortSignal} [options.signal] - An AbortSignal to cancel async event emission.
|
|
27
|
+
* @param {boolean|null} [options.raiseError] - Controls error handling behavior:
|
|
28
|
+
* - `true`: Always throw listener errors immediately.
|
|
29
|
+
* - `false`: Silently swallow listener errors.
|
|
30
|
+
* - `null`: Throw only for 'error' events with no error listeners (Node.js default).
|
|
31
|
+
* - `undefined` (default): Same as `false` for emitAsync.
|
|
26
32
|
* @returns {import('./event-emitter').EventEmitter} A proxy object representing the configured EventEmitter.
|
|
27
33
|
*/
|
|
28
34
|
configure(options) {
|
|
@@ -44,7 +50,7 @@ function getEventableMethods(aClass) {
|
|
|
44
50
|
},
|
|
45
51
|
/**
|
|
46
52
|
* Sets the configuration options for the EventEmitter instance.
|
|
47
|
-
* @param {Object} options - Configuration options for the emitter (e.g., asyncMode, resultMode, maxListeners).
|
|
53
|
+
* @param {Object} options - Configuration options for the emitter (e.g., asyncMode, resultMode, maxListeners, raiseError).
|
|
48
54
|
* @returns {import('./event-emitter').EventEmitter} The EventEmitter instance for chaining.
|
|
49
55
|
*/
|
|
50
56
|
setEmitterOptions(options) {
|
|
@@ -68,7 +74,7 @@ function getEventableMethods(aClass) {
|
|
|
68
74
|
* Adds a listener function to the specified event type.
|
|
69
75
|
* @param {string|RegExp} type - The event type to listen for.
|
|
70
76
|
* @param {Function} listener - The listener function to be called when the event is emitted.
|
|
71
|
-
* @param {number|'first'|'last'} [index] - The index at which to insert the listener.
|
|
77
|
+
* @param {number|'first'|'last'} [index] - The index at which to insert the listener.
|
|
72
78
|
* - 'first' or -Infinity: Adds to the "Head" zone. The first listener added as 'first' is placed at the very front.
|
|
73
79
|
* - 'last' or Infinity: Adds to the "Tail" zone. The first listener added as 'last' will always be the very last one to execute.
|
|
74
80
|
* - number: Inserts at the specified index within the "Body" (normal) zone.
|
|
@@ -95,8 +101,8 @@ function getEventableMethods(aClass) {
|
|
|
95
101
|
if (isRegExp(type)) {
|
|
96
102
|
data = data[_consts.RegExpEventSymbol] || (data[_consts.RegExpEventSymbol] = create(null));
|
|
97
103
|
}
|
|
98
|
-
const isFirst = index ===
|
|
99
|
-
const isLast = index ===
|
|
104
|
+
const isFirst = index === -Infinity || index === 'first';
|
|
105
|
+
const isLast = index === Infinity || index === 'last';
|
|
100
106
|
if (!data[type]) {
|
|
101
107
|
if (isFirst || isLast || typeof index === 'number') {
|
|
102
108
|
data[type] = [listener];
|
|
@@ -158,11 +164,14 @@ function getEventableMethods(aClass) {
|
|
|
158
164
|
}
|
|
159
165
|
let fired = false;
|
|
160
166
|
const self = this;
|
|
161
|
-
|
|
167
|
+
function _once() {
|
|
162
168
|
self.off(type, _once);
|
|
163
169
|
if (!fired) {
|
|
164
170
|
fired = true;
|
|
165
|
-
|
|
171
|
+
const result = listener.apply(this, arguments);
|
|
172
|
+
if (result instanceof Promise) {
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
166
175
|
}
|
|
167
176
|
}
|
|
168
177
|
_once.listener = listener;
|
|
@@ -184,6 +193,8 @@ function getEventableMethods(aClass) {
|
|
|
184
193
|
const listeners = r.listeners;
|
|
185
194
|
const evt = (0, _event.Event)(this, r.type);
|
|
186
195
|
const errs = [];
|
|
196
|
+
const opts = _getOptions(this);
|
|
197
|
+
let _throwErr;
|
|
187
198
|
try {
|
|
188
199
|
let i = 0;
|
|
189
200
|
let listener;
|
|
@@ -194,6 +205,9 @@ function getEventableMethods(aClass) {
|
|
|
194
205
|
break;
|
|
195
206
|
}
|
|
196
207
|
} catch (err) {
|
|
208
|
+
if (opts.raiseError === true) {
|
|
209
|
+
throw err;
|
|
210
|
+
}
|
|
197
211
|
errs.push({
|
|
198
212
|
err: err,
|
|
199
213
|
listener: listener
|
|
@@ -204,13 +218,20 @@ function getEventableMethods(aClass) {
|
|
|
204
218
|
if (errs.length) {
|
|
205
219
|
for (let i = 0; i < errs.length; i++) {
|
|
206
220
|
const it = errs[i];
|
|
221
|
+
if (r.type === 'error') {
|
|
222
|
+
throw it.err;
|
|
223
|
+
}
|
|
207
224
|
this.emit('error', it.err, 'notify', r.type, it.listener, args);
|
|
208
225
|
}
|
|
209
226
|
}
|
|
210
227
|
} finally {
|
|
211
|
-
|
|
212
|
-
|
|
228
|
+
if (r.type === 'error' && opts.raiseError === null) {
|
|
229
|
+
const err = args[0];
|
|
230
|
+
_throwErr = err instanceof Error ? err : new Error(UnCAUGHT_ERR);
|
|
231
|
+
}
|
|
213
232
|
}
|
|
233
|
+
if (_throwErr) throw _throwErr;
|
|
234
|
+
return evt.end();
|
|
214
235
|
},
|
|
215
236
|
/**
|
|
216
237
|
* Asynchronously emits the specified event type with the given arguments.
|
|
@@ -219,6 +240,10 @@ function getEventableMethods(aClass) {
|
|
|
219
240
|
*/
|
|
220
241
|
async emitAsync(/* type, msg , ... */
|
|
221
242
|
) {
|
|
243
|
+
const options = Object.assign({}, this._emitterOptions, this._eeRuntimeOptions);
|
|
244
|
+
if (options.signal && options.signal.aborted) {
|
|
245
|
+
throw (0, _consts.createAbortError)();
|
|
246
|
+
}
|
|
222
247
|
const r = _emit.apply(this, arguments);
|
|
223
248
|
if (!r) {
|
|
224
249
|
return;
|
|
@@ -226,13 +251,29 @@ function getEventableMethods(aClass) {
|
|
|
226
251
|
const args = r.args;
|
|
227
252
|
const listeners = r.listeners;
|
|
228
253
|
const evt = (0, _event.Event)(this, r.type);
|
|
229
|
-
|
|
254
|
+
let _throwErr;
|
|
230
255
|
try {
|
|
231
256
|
await _executeAsync.call(this, listeners, evt, args, options);
|
|
232
|
-
}
|
|
233
|
-
|
|
257
|
+
} catch (err) {
|
|
258
|
+
if (err && err.name === 'AbortError') throw err;
|
|
259
|
+
if (options.raiseError === true) throw err;
|
|
260
|
+
if (r.type === 'error') {
|
|
261
|
+
Object.defineProperty(err, '_errorInError', {
|
|
262
|
+
value: true,
|
|
263
|
+
enumerable: false
|
|
264
|
+
});
|
|
265
|
+
throw err;
|
|
266
|
+
}
|
|
267
|
+
if (err && err._errorInError) throw err;
|
|
268
|
+
// Other unexpected errors: still return the event result
|
|
234
269
|
return evt.end();
|
|
235
270
|
}
|
|
271
|
+
if (r.type === 'error' && options.raiseError === null) {
|
|
272
|
+
const err = args[0];
|
|
273
|
+
_throwErr = err instanceof Error ? err : new Error(UnCAUGHT_ERR);
|
|
274
|
+
}
|
|
275
|
+
if (_throwErr) throw _throwErr;
|
|
276
|
+
return evt.end();
|
|
236
277
|
},
|
|
237
278
|
setMaxListeners(n) {
|
|
238
279
|
if (!(0, _utilEx.isNumber)(n) || n < 0 || isNaN(n)) {
|
|
@@ -408,6 +449,15 @@ function getEventableMethods(aClass) {
|
|
|
408
449
|
};
|
|
409
450
|
}
|
|
410
451
|
var _default = exports.default = getEventableMethods;
|
|
452
|
+
/**
|
|
453
|
+
* Merges instance-level emitter options with per-call runtime options.
|
|
454
|
+
* Runtime options (set via configure()) take precedence.
|
|
455
|
+
* @param {Object} self - The emitter instance.
|
|
456
|
+
* @returns {Object} Merged options.
|
|
457
|
+
*/
|
|
458
|
+
function _getOptions(self) {
|
|
459
|
+
return Object.assign({}, self._emitterOptions, self._eeRuntimeOptions);
|
|
460
|
+
}
|
|
411
461
|
function _emit(type, msg) {
|
|
412
462
|
const data = this._events;
|
|
413
463
|
let listeners;
|
|
@@ -423,8 +473,26 @@ function _emit(type, msg) {
|
|
|
423
473
|
args.push(msg);
|
|
424
474
|
}
|
|
425
475
|
}
|
|
476
|
+
if (type === 'error') {
|
|
477
|
+
const opts = _getOptions(this);
|
|
478
|
+
// raiseError: true → always throw, bypass listener dispatch
|
|
479
|
+
if (opts.raiseError === true) {
|
|
480
|
+
/* v8 ignore next — msg is guaranteed truthy here; falsy case is caught by earlier !msg check */
|
|
481
|
+
if (!(msg instanceof Error)) {
|
|
482
|
+
msg = new Error(msg ? UnCAUGHT_ERR + msg : UnCAUGHT_ERR);
|
|
483
|
+
}
|
|
484
|
+
throw msg;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
426
487
|
// If there is no 'error' event listener then throw.
|
|
427
488
|
if (!listeners && type === 'error') {
|
|
489
|
+
const opts = _getOptions(this);
|
|
490
|
+
// raiseError: false → silently return, never throw
|
|
491
|
+
if (opts.raiseError === false) {
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
// raiseError: undefined / null → Node.js default behavior
|
|
495
|
+
/* v8 ignore next — msg is guaranteed truthy here; falsy case is caught by earlier !msg check */
|
|
428
496
|
if (!(msg instanceof Error)) {
|
|
429
497
|
msg = new Error(msg ? UnCAUGHT_ERR + msg : UnCAUGHT_ERR);
|
|
430
498
|
}
|
|
@@ -481,9 +549,56 @@ function _notify(listener, evt, args) {
|
|
|
481
549
|
}
|
|
482
550
|
return result;
|
|
483
551
|
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Creates a promise that rejects with AbortError when the given AbortSignal fires.
|
|
555
|
+
* Sets evt.aborted = true before rejecting.
|
|
556
|
+
* @param {AbortSignal} signal - The abort signal to listen on.
|
|
557
|
+
* @param {import('./event').Event} evt - The event object to mark as aborted.
|
|
558
|
+
* @returns {Promise<never>} A promise that rejects with AbortError on abort.
|
|
559
|
+
*/
|
|
560
|
+
function _createAbortPromise(signal, evt) {
|
|
561
|
+
return new Promise((_, reject) => {
|
|
562
|
+
if (signal.aborted) {
|
|
563
|
+
evt.aborted = true;
|
|
564
|
+
reject((0, _consts.createAbortError)());
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
const onAbort = () => {
|
|
568
|
+
evt.aborted = true;
|
|
569
|
+
reject((0, _consts.createAbortError)());
|
|
570
|
+
};
|
|
571
|
+
signal.addEventListener('abort', onAbort, {
|
|
572
|
+
once: true
|
|
573
|
+
});
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Executes all listener promises in parallel and assigns the result to evt.
|
|
579
|
+
* @param {Promise[]} promises - The listener promises to execute.
|
|
580
|
+
* @param {import('./event').Event} evt - The event object.
|
|
581
|
+
* @param {string} resultMode - 'last', 'first', or 'collect'.
|
|
582
|
+
*/
|
|
583
|
+
async function _runParallelListeners(promises, evt, resultMode) {
|
|
584
|
+
if (resultMode === 'collect') {
|
|
585
|
+
const wrapped = promises.map(p => p.catch(() => undefined));
|
|
586
|
+
const results = await Promise.all(wrapped);
|
|
587
|
+
evt.result = results;
|
|
588
|
+
} else if (resultMode === 'first') {
|
|
589
|
+
try {
|
|
590
|
+
await Promise.any(promises.map(p => p.then(res => res === undefined ? Promise.reject() : res)));
|
|
591
|
+
} catch (e) {
|
|
592
|
+
// If all rejected or returned undefined, ignore
|
|
593
|
+
}
|
|
594
|
+
} else {
|
|
595
|
+
await Promise.all(promises.map(p => p.catch(() => undefined)));
|
|
596
|
+
}
|
|
597
|
+
}
|
|
484
598
|
async function _executeAsync(listeners, evt, args, options) {
|
|
485
599
|
const asyncMode = options.asyncMode || 'serial';
|
|
486
600
|
const resultMode = options.resultMode || 'last';
|
|
601
|
+
const signal = options.signal;
|
|
487
602
|
const errs = [];
|
|
488
603
|
if (resultMode === 'collect') {
|
|
489
604
|
evt.result = [];
|
|
@@ -501,6 +616,9 @@ async function _executeAsync(listeners, evt, args, options) {
|
|
|
501
616
|
}
|
|
502
617
|
return result;
|
|
503
618
|
} catch (err) {
|
|
619
|
+
if (options.raiseError === true) {
|
|
620
|
+
throw err;
|
|
621
|
+
}
|
|
504
622
|
errs.push({
|
|
505
623
|
err,
|
|
506
624
|
listener
|
|
@@ -510,27 +628,44 @@ async function _executeAsync(listeners, evt, args, options) {
|
|
|
510
628
|
};
|
|
511
629
|
if (asyncMode === 'parallel') {
|
|
512
630
|
const promises = listeners.map(listener => notifyListener(listener));
|
|
513
|
-
if (
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
// If all rejected or returned undefined, ignore
|
|
631
|
+
if (options.raiseError === true) {
|
|
632
|
+
// Collect all rejections into AggregateError (all parallel listeners start simultaneously)
|
|
633
|
+
const settled = signal ? await Promise.race([Promise.allSettled(promises), _createAbortPromise(signal, evt)]) : await Promise.allSettled(promises);
|
|
634
|
+
const rejections = settled.filter(r => r.status === 'rejected').map(r => r.reason);
|
|
635
|
+
if (rejections.length > 0) {
|
|
636
|
+
throw rejections.length === 1 ? rejections[0] : new AggregateError(rejections);
|
|
520
637
|
}
|
|
521
638
|
} else {
|
|
522
|
-
|
|
639
|
+
const runParallel = () => _runParallelListeners(promises, evt, resultMode);
|
|
640
|
+
if (signal) {
|
|
641
|
+
await Promise.race([runParallel(), _createAbortPromise(signal, evt)]);
|
|
642
|
+
} else {
|
|
643
|
+
await runParallel();
|
|
644
|
+
}
|
|
523
645
|
}
|
|
524
646
|
} else {
|
|
525
|
-
// Serial mode
|
|
647
|
+
// Serial mode
|
|
648
|
+
// Only race against abort signal when raiseError is explicitly set;
|
|
649
|
+
// default (undefined) keeps old behavior: check signal between listeners only.
|
|
650
|
+
const canRaceAbort = signal && 'raiseError' in options;
|
|
526
651
|
for (const listener of listeners) {
|
|
652
|
+
// Check AbortSignal before firing the next listener
|
|
653
|
+
if (signal && signal.aborted) {
|
|
654
|
+
evt.aborted = true;
|
|
655
|
+
break;
|
|
656
|
+
}
|
|
527
657
|
try {
|
|
528
|
-
const result = await notifyListener(listener);
|
|
658
|
+
const result = canRaceAbort ? await Promise.race([notifyListener(listener), _createAbortPromise(signal, evt)]) : await notifyListener(listener);
|
|
529
659
|
if (resultMode === 'collect') {
|
|
530
660
|
evt.result.push(result);
|
|
531
661
|
}
|
|
532
662
|
if (evt.stopped || resultMode === 'first' && evt.resolved) break;
|
|
533
663
|
} catch (err) {
|
|
664
|
+
if (canRaceAbort && err && err.name === 'AbortError') {
|
|
665
|
+
evt.aborted = true;
|
|
666
|
+
throw err;
|
|
667
|
+
}
|
|
668
|
+
if (options.raiseError === true) throw err;
|
|
534
669
|
if (resultMode === 'collect') {
|
|
535
670
|
evt.result.push(undefined);
|
|
536
671
|
}
|
|
@@ -540,7 +675,24 @@ async function _executeAsync(listeners, evt, args, options) {
|
|
|
540
675
|
if (errs.length) {
|
|
541
676
|
for (let i = 0; i < errs.length; i++) {
|
|
542
677
|
const it = errs[i];
|
|
543
|
-
|
|
678
|
+
if (evt.type === 'error') {
|
|
679
|
+
throw it.err;
|
|
680
|
+
}
|
|
681
|
+
// Temporarily clear signal so error re-emission is not blocked by an already-aborted signal
|
|
682
|
+
const savedSignal = this._eeRuntimeOptions && this._eeRuntimeOptions.signal;
|
|
683
|
+
if (savedSignal) {
|
|
684
|
+
delete this._eeRuntimeOptions.signal;
|
|
685
|
+
}
|
|
686
|
+
try {
|
|
687
|
+
await this.emitAsync('error', it.err, 'notify', evt.type, it.listener, args);
|
|
688
|
+
} finally {
|
|
689
|
+
if (savedSignal) {
|
|
690
|
+
this._eeRuntimeOptions.signal = savedSignal;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
544
693
|
}
|
|
545
694
|
}
|
|
695
|
+
if (evt.aborted) {
|
|
696
|
+
throw (0, _consts.createAbortError)();
|
|
697
|
+
}
|
|
546
698
|
}
|