events-ex 2.1.1 → 2.3.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/CHANGELOG.md +20 -0
- package/README.cn.md +119 -170
- package/README.md +140 -150
- package/docs/README.md +143 -149
- package/docs/all-off/README.md +17 -0
- package/docs/all-off/functions/allOff.md +33 -0
- package/docs/consts/README.md +12 -0
- package/docs/consts/variables/RegExpEventSymbol.md +11 -0
- package/docs/consts/variables/states.md +29 -0
- package/docs/default-methods/README.md +17 -0
- package/docs/default-methods/functions/getEventableMethods.md +309 -0
- package/docs/event/README.md +17 -0
- package/docs/event/classes/Event.md +143 -0
- package/docs/event-emitter/README-1.md +17 -0
- package/docs/event-emitter/README.md +17 -0
- package/docs/event-emitter/classes/EventEmitter-1.md +29 -0
- package/docs/event-emitter/classes/EventEmitter.md +369 -0
- package/docs/eventable/README.md +17 -0
- package/docs/eventable/functions/eventable.md +82 -0
- package/docs/has-listeners/README.md +17 -0
- package/docs/has-listeners/functions/hasListeners.md +38 -0
- package/docs/index/README.md +85 -0
- package/docs/modules.md +24 -23
- package/docs/pipe/README.md +17 -0
- package/docs/pipe/functions/pipe.md +41 -0
- package/docs/pipe-async/README.md +17 -0
- package/docs/pipe-async/functions/pipeAsync.md +41 -0
- package/docs/unify/README.md +17 -0
- package/docs/unify/functions/unify.md +35 -0
- package/docs/util/array-remove/README.md +17 -0
- package/docs/util/array-remove/functions/remove.md +21 -0
- package/docs/util/object-for-each/README.md +17 -0
- package/docs/util/object-for-each/functions/forEach.md +29 -0
- package/docs/util/promise-any/README.md +11 -0
- package/docs/util/promise-any/variables/default.md +9 -0
- package/docs/util/string-pad/README.md +17 -0
- package/docs/util/string-pad/functions/pad.md +25 -0
- package/docs/util/to-int/README.md +17 -0
- package/docs/util/to-int/functions/toInt.md +21 -0
- package/docs/util/valid-callable/README.md +17 -0
- package/docs/util/valid-callable/functions/validCallable.md +21 -0
- package/docs/util/valid-object/README.md +17 -0
- package/docs/util/valid-object/functions/validObject.md +21 -0
- package/docs/wrap-event-emitter/README.md +21 -0
- package/docs/wrap-event-emitter/functions/wrapEventEmitter.md +33 -0
- package/docs/wrap-event-emitter/variables/methods.md +11 -0
- package/lib/all-off.d.ts +1 -1
- package/lib/all-off.js +1 -1
- package/lib/default-methods.d.ts +38 -7
- package/lib/default-methods.js +155 -32
- package/lib/event-emitter.d.ts +12 -2
- package/lib/event.d.ts +9 -3
- package/lib/event.js +6 -0
- package/lib/eventable.js +4 -1
- package/lib/pipe-async.d.ts +4 -1
- package/lib/pipe-async.js +39 -5
- package/lib/pipe.d.ts +1 -1
- package/lib/pipe.js +1 -1
- package/lib/unify.d.ts +1 -1
- package/lib/unify.js +1 -1
- package/lib/util/array-remove.js +1 -1
- package/lib/util/object-for-each.js +1 -1
- package/lib/util/promise-any.d.ts +1 -0
- package/lib/util/promise-any.js +44 -0
- package/lib/util/string-pad.js +1 -1
- package/lib/wrap-event-emitter.d.ts +2 -1
- package/lib/wrap-event-emitter.js +24 -3
- package/package.json +17 -17
- package/src/default-methods.js +159 -26
- package/src/event-emitter.d.ts +12 -2
- package/src/event.js +6 -0
- package/src/eventable.js +3 -0
- package/src/pipe-async.js +64 -30
- package/src/util/promise-any.js +45 -0
- package/src/wrap-event-emitter.js +23 -18
- package/docs/.nojekyll +0 -1
- package/docs/classes/event.Event.md +0 -141
- package/docs/classes/event_emitter-1.EventEmitter.md +0 -29
- package/docs/classes/event_emitter.EventEmitter.md +0 -352
- package/docs/modules/all_off.md +0 -44
- package/docs/modules/consts.md +0 -39
- package/docs/modules/default_methods.md +0 -51
- package/docs/modules/event.md +0 -19
- package/docs/modules/event_emitter-1.md +0 -19
- package/docs/modules/event_emitter.md +0 -19
- package/docs/modules/eventable.md +0 -92
- package/docs/modules/has_listeners.md +0 -49
- package/docs/modules/index.md +0 -99
- package/docs/modules/pipe.md +0 -49
- package/docs/modules/pipe_async.md +0 -49
- package/docs/modules/unify.md +0 -46
- package/docs/modules/util_array_remove.md +0 -39
- package/docs/modules/util_object_for_each.md +0 -41
- package/docs/modules/util_string_pad.md +0 -40
- package/docs/modules/util_to_int.md +0 -39
- package/docs/modules/util_valid_callable.md +0 -39
- package/docs/modules/util_valid_object.md +0 -39
- package/docs/modules/wrap_event_emitter.md +0 -57
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "events-ex",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Browser-friendly enhanced events most compatible with standard node.js, it's powerful eventable ability.",
|
|
5
5
|
"contributors": [
|
|
6
6
|
{
|
|
@@ -37,25 +37,25 @@
|
|
|
37
37
|
"main": "./lib/index.js",
|
|
38
38
|
"module": "./src/index.js",
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"custom-ability": "^2.0
|
|
41
|
-
"util-ex": "^2.
|
|
40
|
+
"custom-ability": "^2.1.0",
|
|
41
|
+
"util-ex": "^2.5.1"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@antfu/eslint-config": "^
|
|
45
|
-
"@babel/cli": "^7.
|
|
46
|
-
"@babel/core": "^7.
|
|
44
|
+
"@antfu/eslint-config": "^7.7.0",
|
|
45
|
+
"@babel/cli": "^7.28.6",
|
|
46
|
+
"@babel/core": "^7.29.0",
|
|
47
47
|
"@babel/plugin-proposal-dynamic-import": "^7.18.6",
|
|
48
|
-
"@babel/plugin-transform-modules-commonjs": "^7.
|
|
49
|
-
"@babel/register": "^7.
|
|
50
|
-
"chai": "~4.3.
|
|
51
|
-
"eslint": "^
|
|
52
|
-
"eslint-config-prettier": "^
|
|
53
|
-
"eslint-plugin-tsdoc": "^0.2
|
|
54
|
-
"mocha": "^
|
|
55
|
-
"prettier": "^3.
|
|
56
|
-
"typedoc": "^0.
|
|
57
|
-
"typedoc-plugin-markdown": "^
|
|
58
|
-
"typescript": "
|
|
48
|
+
"@babel/plugin-transform-modules-commonjs": "^7.28.6",
|
|
49
|
+
"@babel/register": "^7.28.6",
|
|
50
|
+
"chai": "~4.3.10",
|
|
51
|
+
"eslint": "^10.0.3",
|
|
52
|
+
"eslint-config-prettier": "^10.1.8",
|
|
53
|
+
"eslint-plugin-tsdoc": "^0.5.2",
|
|
54
|
+
"mocha": "^11.7.5",
|
|
55
|
+
"prettier": "^3.8.1",
|
|
56
|
+
"typedoc": "^0.28.17",
|
|
57
|
+
"typedoc-plugin-markdown": "^4.10.0",
|
|
58
|
+
"typescript": "~5.7.3"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
61
|
"build": "npm run build.cjs && npm run build.ts && npm run doc.md",
|
package/src/default-methods.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {defineProperty, isArray, isFunction, isNumber, isObject, isRegExp as _isRegExp, isUndefined, isRegExpStr, toRegExp } from 'util-ex'
|
|
2
|
+
import './util/promise-any'
|
|
2
3
|
import {RegExpEventSymbol} from './consts'
|
|
3
4
|
import {Event} from './event';
|
|
4
5
|
|
|
@@ -13,11 +14,59 @@ function isRegExp(value) {
|
|
|
13
14
|
|
|
14
15
|
export function getEventableMethods(aClass) {
|
|
15
16
|
return {
|
|
17
|
+
/**
|
|
18
|
+
* Configures the event emitter with specified options using a Fluent API.
|
|
19
|
+
* @param {Object} options - Configuration options for event emission.
|
|
20
|
+
* @param {string} [options.asyncMode='serial'] - The mode of asynchronous emission ('serial' or 'parallel').
|
|
21
|
+
* @param {string} [options.resultMode='last'] - The strategy for handling multiple return values ('last', 'first', 'collect').
|
|
22
|
+
* @returns {import('./event-emitter').EventEmitter} A proxy object representing the configured EventEmitter.
|
|
23
|
+
*/
|
|
24
|
+
configure(options) {
|
|
25
|
+
const proxy = Object.create(this)
|
|
26
|
+
const mergedOptions = Object.assign({}, this._eeRuntimeOptions, options)
|
|
27
|
+
defineProperty(proxy, '_eeRuntimeOptions', mergedOptions)
|
|
28
|
+
return proxy
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A shortcut for parallel configuration.
|
|
33
|
+
* @param {string} [resultMode='last'] - The strategy for handling multiple return values.
|
|
34
|
+
* @returns {import('./event-emitter').EventEmitter} A proxy object representing the configured EventEmitter.
|
|
35
|
+
*/
|
|
36
|
+
parallel(resultMode) {
|
|
37
|
+
return this.configure({asyncMode: 'parallel', resultMode: resultMode || 'last'})
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Sets the configuration options for the EventEmitter instance.
|
|
42
|
+
* @param {Object} options - Configuration options for the emitter (e.g., asyncMode, resultMode, maxListeners).
|
|
43
|
+
* @returns {import('./event-emitter').EventEmitter} The EventEmitter instance for chaining.
|
|
44
|
+
*/
|
|
45
|
+
setEmitterOptions(options) {
|
|
46
|
+
if (!isObject(options)) {return this}
|
|
47
|
+
let data
|
|
48
|
+
if (!this.hasOwnProperty('_emitterOptions')) {
|
|
49
|
+
data = create(null)
|
|
50
|
+
defineProperty(this, '_emitterOptions', data)
|
|
51
|
+
} else {
|
|
52
|
+
data = this._emitterOptions
|
|
53
|
+
}
|
|
54
|
+
Object.assign(data, options)
|
|
55
|
+
if (!isUndefined(options.maxListeners) && isFunction(this.setMaxListeners)) {
|
|
56
|
+
this.setMaxListeners(options.maxListeners)
|
|
57
|
+
}
|
|
58
|
+
return this
|
|
59
|
+
},
|
|
60
|
+
|
|
16
61
|
/**
|
|
17
62
|
* Adds a listener function to the specified event type.
|
|
18
63
|
* @param {string|RegExp} type - The event type to listen for.
|
|
19
64
|
* @param {Function} listener - The listener function to be called when the event is emitted.
|
|
20
|
-
* @param {number} [index] - The index at which to insert the listener.
|
|
65
|
+
* @param {number|'first'|'last'} [index] - The index at which to insert the listener.
|
|
66
|
+
* - 'first' or -Infinity: Adds to the "Head" zone. The first listener added as 'first' is placed at the very front.
|
|
67
|
+
* - 'last' or Infinity: Adds to the "Tail" zone. The first listener added as 'last' will always be the very last one to execute.
|
|
68
|
+
* - number: Inserts at the specified index within the "Body" (normal) zone.
|
|
69
|
+
* If not specified, the listener is added to the end of the "Body" zone.
|
|
21
70
|
* @returns {import('./event-emitter').EventEmitter} The EventEmitter instance to allow chaining.
|
|
22
71
|
* @throws {TypeError} If the listener is not a function.
|
|
23
72
|
*/
|
|
@@ -38,16 +87,38 @@ export function getEventableMethods(aClass) {
|
|
|
38
87
|
if (isRegExp(type)) {
|
|
39
88
|
data = data[RegExpEventSymbol] || (data[RegExpEventSymbol] = create(null))
|
|
40
89
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
90
|
+
|
|
91
|
+
const isFirst = index === 'first' || index === -Infinity
|
|
92
|
+
const isLast = index === 'last' || index === Infinity
|
|
93
|
+
|
|
94
|
+
if (!data[type]) {
|
|
95
|
+
if (isFirst || isLast || typeof index === 'number') {
|
|
96
|
+
data[type] = [listener]
|
|
97
|
+
if (isFirst) data[type]._headCount = 1
|
|
98
|
+
if (isLast) data[type]._tailCount = 1
|
|
46
99
|
} else {
|
|
47
|
-
data[type]
|
|
100
|
+
data[type] = listener
|
|
48
101
|
}
|
|
49
102
|
} else {
|
|
50
|
-
|
|
103
|
+
if (isFunction(data[type])) {
|
|
104
|
+
data[type] = [data[type]]
|
|
105
|
+
}
|
|
106
|
+
const listeners = data[type]
|
|
107
|
+
const headCount = listeners._headCount || 0
|
|
108
|
+
const tailCount = listeners._tailCount || 0
|
|
109
|
+
|
|
110
|
+
if (isFirst) {
|
|
111
|
+
listeners.splice(headCount, 0, listener)
|
|
112
|
+
listeners._headCount = headCount + 1
|
|
113
|
+
} else if (isLast) {
|
|
114
|
+
listeners.splice(listeners.length - tailCount, 0, listener)
|
|
115
|
+
listeners._tailCount = tailCount + 1
|
|
116
|
+
} else if (typeof index === 'number' && !isNaN(index)) {
|
|
117
|
+
const pos = Math.min(Math.max(headCount + index, headCount), listeners.length - tailCount)
|
|
118
|
+
listeners.splice(pos, 0, listener)
|
|
119
|
+
} else {
|
|
120
|
+
listeners.splice(listeners.length - tailCount, 0, listener)
|
|
121
|
+
}
|
|
51
122
|
}
|
|
52
123
|
// Check for listener leak
|
|
53
124
|
if (isObject(data[type]) && !data[type].warned) {
|
|
@@ -74,7 +145,11 @@ export function getEventableMethods(aClass) {
|
|
|
74
145
|
* Adds a one-time listener function to the specified event type.
|
|
75
146
|
* @param {string|RegExp} type - The event type to listen for.
|
|
76
147
|
* @param {Function} listener - The listener function to be called once when the event is emitted.
|
|
77
|
-
* @param {number} [index] - The index at which to insert the listener.
|
|
148
|
+
* @param {number|'first'|'last'} [index] - The index at which to insert the listener.
|
|
149
|
+
* - 'first' or -Infinity: Adds to the "Head" zone. The first listener added as 'first' is placed at the very front.
|
|
150
|
+
* - 'last' or Infinity: Adds to the "Tail" zone. The first listener added as 'last' will always be the very last one to execute.
|
|
151
|
+
* - number: Inserts at the specified index within the "Body" (normal) zone.
|
|
152
|
+
* If not specified, the listener is added to the end of the "Body" zone.
|
|
78
153
|
* @returns {import('./event-emitter').EventEmitter} The EventEmitter instance to allow chaining.
|
|
79
154
|
* @throws {TypeError} If the listener is not a function.
|
|
80
155
|
*/
|
|
@@ -143,22 +218,9 @@ export function getEventableMethods(aClass) {
|
|
|
143
218
|
const args = r.args
|
|
144
219
|
const listeners = r.listeners
|
|
145
220
|
const evt = Event(this, r.type)
|
|
146
|
-
const
|
|
221
|
+
const options = Object.assign({}, this._emitterOptions, this._eeRuntimeOptions)
|
|
147
222
|
try {
|
|
148
|
-
|
|
149
|
-
try {
|
|
150
|
-
await _notify(listener, evt, args);
|
|
151
|
-
if (evt.stopped) {break}
|
|
152
|
-
} catch(err) {
|
|
153
|
-
errs.push({err: err, listener: listener})
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
if (errs.length) {
|
|
157
|
-
for (let i=0;i<errs.length;i++) {
|
|
158
|
-
const it = errs[i]
|
|
159
|
-
this.emit('error', it.err, 'notify', r.type, it.listener, args)
|
|
160
|
-
}
|
|
161
|
-
}
|
|
223
|
+
await _executeAsync.call(this, listeners, evt, args, options)
|
|
162
224
|
} finally {
|
|
163
225
|
// eslint-disable-next-line no-unsafe-finally
|
|
164
226
|
return evt.end()
|
|
@@ -243,10 +305,16 @@ export function getEventableMethods(aClass) {
|
|
|
243
305
|
if (candidate === listener || candidate.listener === listener) {break}
|
|
244
306
|
}
|
|
245
307
|
if (i < 0) {return this}
|
|
308
|
+
|
|
309
|
+
if (listeners._headCount && i < listeners._headCount) {
|
|
310
|
+
listeners._headCount--
|
|
311
|
+
} else if (listeners._tailCount && i >= listeners.length - listeners._tailCount) {
|
|
312
|
+
listeners._tailCount--
|
|
313
|
+
}
|
|
314
|
+
|
|
246
315
|
if (listeners.length === 1) {
|
|
247
|
-
listeners.length = 0
|
|
248
316
|
delete data[type]
|
|
249
|
-
} else if (listeners.length === 2) {
|
|
317
|
+
} else if (listeners.length === 2 && !listeners._headCount && !listeners._tailCount) {
|
|
250
318
|
data[type] = listeners[(i ? 0 : 1)]
|
|
251
319
|
listeners.length = 1
|
|
252
320
|
} else {
|
|
@@ -381,3 +449,68 @@ function _notify(listener, evt, args) {
|
|
|
381
449
|
}
|
|
382
450
|
return result
|
|
383
451
|
}
|
|
452
|
+
|
|
453
|
+
async function _executeAsync(listeners, evt, args, options) {
|
|
454
|
+
const asyncMode = options.asyncMode || 'serial'
|
|
455
|
+
const resultMode = options.resultMode || 'last'
|
|
456
|
+
const errs = []
|
|
457
|
+
|
|
458
|
+
if (resultMode === 'collect') {
|
|
459
|
+
evt.result = []
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const notifyListener = async (listener) => {
|
|
463
|
+
try {
|
|
464
|
+
const result = await _notify(listener, evt, args)
|
|
465
|
+
if (result !== undefined) {
|
|
466
|
+
if (resultMode === 'first' && !evt.resolved) {
|
|
467
|
+
evt.result = result
|
|
468
|
+
evt.resolved = true
|
|
469
|
+
} else if (resultMode === 'last') {
|
|
470
|
+
evt.result = result
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return result
|
|
474
|
+
} catch (err) {
|
|
475
|
+
errs.push({err, listener})
|
|
476
|
+
throw err
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (asyncMode === 'parallel') {
|
|
481
|
+
const promises = listeners.map(listener => notifyListener(listener))
|
|
482
|
+
if (resultMode === 'collect') {
|
|
483
|
+
evt.result = await Promise.all(promises.map(p => p.catch(() => undefined)))
|
|
484
|
+
} else if (resultMode === 'first') {
|
|
485
|
+
try {
|
|
486
|
+
await Promise.any(promises.map(p => p.then(res => res === undefined ? Promise.reject() : res)))
|
|
487
|
+
} catch (e) {
|
|
488
|
+
// If all rejected or returned undefined, ignore
|
|
489
|
+
}
|
|
490
|
+
} else {
|
|
491
|
+
await Promise.all(promises.map(p => p.catch(() => undefined)))
|
|
492
|
+
}
|
|
493
|
+
} else {
|
|
494
|
+
// Serial mode (default)
|
|
495
|
+
for (const listener of listeners) {
|
|
496
|
+
try {
|
|
497
|
+
const result = await notifyListener(listener)
|
|
498
|
+
if (resultMode === 'collect') {
|
|
499
|
+
evt.result.push(result)
|
|
500
|
+
}
|
|
501
|
+
if (evt.stopped || (resultMode === 'first' && evt.resolved)) break
|
|
502
|
+
} catch (err) {
|
|
503
|
+
if (resultMode === 'collect') {
|
|
504
|
+
evt.result.push(undefined)
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (errs.length) {
|
|
511
|
+
for (let i = 0; i < errs.length; i++) {
|
|
512
|
+
const it = errs[i]
|
|
513
|
+
this.emit('error', it.err, 'notify', evt.type, it.listener, args)
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
package/src/event-emitter.d.ts
CHANGED
|
@@ -10,18 +10,28 @@ export class EventEmitter {
|
|
|
10
10
|
* Adds a listener function to the specified event type.
|
|
11
11
|
* @param {string|RegExp} type - The event type to listen for.
|
|
12
12
|
* @param {Function} listener - The listener function to be called when the event is emitted.
|
|
13
|
+
* @param {number|'first'|'last'} [index] - The index at which to insert the listener.
|
|
14
|
+
* - 'first' or -Infinity: adds to the beginning of the listeners (stay at the front).
|
|
15
|
+
* - 'last' or Infinity: adds to the end of the listeners (stay at the back).
|
|
16
|
+
* - number: inserts at the specified index within the normal listeners zone.
|
|
17
|
+
* If not specified, the listener will be added at the end of the normal listeners.
|
|
13
18
|
* @returns {EventEmitter} The EventEmitter instance to allow chaining.
|
|
14
19
|
* @throws {TypeError} If the listener is not a function.
|
|
15
20
|
*/
|
|
16
|
-
on(eventName: string|RegExp, listener: ListenerCallbackFunc): EventEmitter;
|
|
21
|
+
on(eventName: string|RegExp, listener: ListenerCallbackFunc, index?: number|'first'|'last'): EventEmitter;
|
|
17
22
|
/**
|
|
18
23
|
* Adds a one-time listener function to the specified event type.
|
|
19
24
|
* @param {string|RegExp} type - The event type to listen for.
|
|
20
25
|
* @param {Function} listener - The listener function to be called once when the event is emitted.
|
|
26
|
+
* @param {number|'first'|'last'} [index] - The index at which to insert the listener.
|
|
27
|
+
* - 'first' or -Infinity: adds to the beginning of the listeners (stay at the front).
|
|
28
|
+
* - 'last' or Infinity: adds to the end of the listeners (stay at the back).
|
|
29
|
+
* - number: inserts at the specified index within the normal listeners zone.
|
|
30
|
+
* If not specified, the listener will be added at the end of the normal listeners.
|
|
21
31
|
* @returns {EventEmitter} The EventEmitter instance to allow chaining.
|
|
22
32
|
* @throws {TypeError} If the listener is not a function.
|
|
23
33
|
*/
|
|
24
|
-
once(eventName: string|RegExp, listener: ListenerCallbackFunc): EventEmitter;
|
|
34
|
+
once(eventName: string|RegExp, listener: ListenerCallbackFunc, index?: number|'first'|'last'): EventEmitter;
|
|
25
35
|
/**
|
|
26
36
|
* Removes a listener function from the specified event type.
|
|
27
37
|
* @param {string|RegExp} type - The event type to remove the listener from.
|
package/src/event.js
CHANGED
|
@@ -30,6 +30,12 @@ Event.prototype.init = function(target, type) {
|
|
|
30
30
|
* @public
|
|
31
31
|
*/
|
|
32
32
|
this.stopped = false
|
|
33
|
+
/**
|
|
34
|
+
* Whether a result has been resolved (for 'first' result mode)
|
|
35
|
+
* @type {boolean}
|
|
36
|
+
* @public
|
|
37
|
+
*/
|
|
38
|
+
this.resolved = false
|
|
33
39
|
/**
|
|
34
40
|
* Keep your event result here if any.
|
|
35
41
|
* @type {*}
|
package/src/eventable.js
CHANGED
|
@@ -25,6 +25,9 @@ function getEventableClass(aClass) {
|
|
|
25
25
|
Eventable.prototype.listenerCount = methods.listenerCount;
|
|
26
26
|
Eventable.prototype.emit = methods.emit;
|
|
27
27
|
Eventable.prototype.emitAsync = methods.emitAsync;
|
|
28
|
+
Eventable.prototype.configure = methods.configure;
|
|
29
|
+
Eventable.prototype.parallel = methods.parallel;
|
|
30
|
+
Eventable.prototype.setEmitterOptions = methods.setEmitterOptions;
|
|
28
31
|
Eventable.prototype.on = methods.on;
|
|
29
32
|
Eventable.prototype.addListener = methods.on;
|
|
30
33
|
Eventable.prototype.off = methods.off;
|
package/src/pipe-async.js
CHANGED
|
@@ -15,39 +15,73 @@ const emit = methods.emitAsync
|
|
|
15
15
|
* @param {import('./event-emitter').EventEmitter} e1 - The first event emitter.
|
|
16
16
|
* @param {import('./event-emitter').EventEmitter} e2 - The second event emitter.
|
|
17
17
|
* @param {string} [name='emitAsync'] - The name of the event to pipe (defaults to 'emitAsync').
|
|
18
|
+
* @param {Object} [options] - Configuration for the pipeline.
|
|
19
|
+
* @param {string} [options.asyncMode='serial'] - The mode of propagation ('serial' or 'parallel').
|
|
20
|
+
* @param {string} [options.resultMode] - Strategy for aggregating results from the pipe chain ('collect', 'first').
|
|
18
21
|
* @returns {Object} - An object with a `close` method that removes the pipeline between the two event emitters.
|
|
19
22
|
* @throws {TypeError} - If either of the arguments is not an event emitter object.
|
|
20
23
|
*/
|
|
21
|
-
export function pipeAsync(e1, e2/* , name */) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
24
|
+
export function pipeAsync(e1, e2/* , name, options */) {
|
|
25
|
+
let pipes
|
|
26
|
+
|
|
27
|
+
(validObject(e1) && validObject(e2))
|
|
28
|
+
let name = arguments[2]
|
|
29
|
+
let options = arguments[3]
|
|
30
|
+
if (typeof name === 'object') {
|
|
31
|
+
options = name
|
|
32
|
+
name = undefined
|
|
33
|
+
}
|
|
34
|
+
if (name === undefined) {name = 'emitAsync'}
|
|
35
|
+
if (!options) {options = {}}
|
|
36
|
+
|
|
37
|
+
const result = {
|
|
38
|
+
close() { arrRemove.call(pipes, e2) }
|
|
39
|
+
};
|
|
40
|
+
if (hasOwnProperty.call(e1, '__eePipes__')) {
|
|
41
|
+
(pipes = e1.__eePipes__).push(e2)
|
|
42
|
+
return result
|
|
43
|
+
}
|
|
44
|
+
defineProperty(e1, '__eePipes__', pipes = [e2])
|
|
45
|
+
let desc = getOwnPropertyDescriptor(e1, name)
|
|
46
|
+
if (!desc) {
|
|
47
|
+
desc = {}
|
|
48
|
+
} else {
|
|
49
|
+
delete desc.get
|
|
50
|
+
delete desc.set
|
|
51
|
+
}
|
|
52
|
+
desc.value = async function () {
|
|
53
|
+
const data = arrFrom(pipes)
|
|
54
|
+
const asyncMode = options.asyncMode || 'serial'
|
|
55
|
+
const resultMode = options.resultMode
|
|
56
|
+
|
|
57
|
+
const forward = async (target, args) => {
|
|
58
|
+
const fn = target[name] || target.emitAsync || emit
|
|
59
|
+
return fn.apply(target, args)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (asyncMode === 'parallel') {
|
|
63
|
+
const promises = [emit.apply(this, arguments)]
|
|
64
|
+
for (let i = 0; i < data.length; ++i) {
|
|
65
|
+
promises.push(forward(data[i], arguments))
|
|
66
|
+
}
|
|
67
|
+
const results = await Promise.all(promises)
|
|
68
|
+
if (resultMode === 'collect') return results
|
|
69
|
+
if (resultMode === 'first') return results.find(r => r !== undefined)
|
|
70
|
+
return results[0] // Default: return main emitter's result
|
|
71
|
+
} else {
|
|
72
|
+
const mainResult = await emit.apply(this, arguments)
|
|
73
|
+
const allResults = [mainResult]
|
|
74
|
+
for (let i = 0; i < data.length; ++i) {
|
|
75
|
+
const res = await forward(data[i], arguments)
|
|
76
|
+
allResults.push(res)
|
|
77
|
+
}
|
|
78
|
+
if (resultMode === 'collect') return allResults
|
|
79
|
+
if (resultMode === 'first') return allResults.find(r => r !== undefined)
|
|
80
|
+
return mainResult // Default: return main emitter's result
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
defineProperty(e1, name, desc.value, desc)
|
|
84
|
+
return result
|
|
51
85
|
};
|
|
52
86
|
|
|
53
87
|
export default pipeAsync
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple polyfill for AggregateError if it doesn't exist.
|
|
3
|
+
*/
|
|
4
|
+
const _AggregateError = typeof AggregateError !== 'undefined'
|
|
5
|
+
? AggregateError
|
|
6
|
+
: function AggregateError(errors, message) {
|
|
7
|
+
const error = new Error(message);
|
|
8
|
+
error.name = 'AggregateError';
|
|
9
|
+
error.errors = errors;
|
|
10
|
+
return error;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Ensures Promise.any exists, or provides a polyfill.
|
|
15
|
+
*/
|
|
16
|
+
if (typeof Promise.any !== 'function') {
|
|
17
|
+
Promise.any = function (promises) {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
promises = Array.from(promises);
|
|
20
|
+
const len = promises.length;
|
|
21
|
+
let errors = [];
|
|
22
|
+
let rejectedCount = 0;
|
|
23
|
+
|
|
24
|
+
if (len === 0) {
|
|
25
|
+
return reject(new _AggregateError(errors, 'All promises were rejected'));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
promises.forEach((promise, index) => {
|
|
29
|
+
Promise.resolve(promise)
|
|
30
|
+
.then((value) => {
|
|
31
|
+
resolve(value);
|
|
32
|
+
})
|
|
33
|
+
.catch((error) => {
|
|
34
|
+
errors[index] = error;
|
|
35
|
+
rejectedCount++;
|
|
36
|
+
if (rejectedCount === len) {
|
|
37
|
+
reject(new _AggregateError(errors, 'All promises were rejected'));
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default Promise.any;
|
|
@@ -6,36 +6,41 @@ const defineProperties = Object.defineProperties;
|
|
|
6
6
|
|
|
7
7
|
export const methods = eventable().methods;
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Minimal set of core methods to be injected into an existing object.
|
|
11
|
+
*/
|
|
9
12
|
const descriptors = {
|
|
10
|
-
on: {
|
|
11
|
-
|
|
12
|
-
},
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
},
|
|
16
|
-
off: {
|
|
17
|
-
value: methods.off
|
|
18
|
-
},
|
|
19
|
-
emit: {
|
|
20
|
-
value: methods.emit
|
|
21
|
-
},
|
|
22
|
-
emitAsync: {
|
|
23
|
-
value: methods.emitAsync
|
|
24
|
-
},
|
|
13
|
+
on: { value: methods.on },
|
|
14
|
+
once: { value: methods.once },
|
|
15
|
+
off: { value: methods.off },
|
|
16
|
+
emit: { value: methods.emit },
|
|
17
|
+
emitAsync: { value: methods.emitAsync },
|
|
18
|
+
setEmitterOptions: { value: methods.setEmitterOptions },
|
|
25
19
|
};
|
|
26
20
|
|
|
27
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Full set of all available methods from eventable for standalone instances.
|
|
23
|
+
*/
|
|
24
|
+
const fullDescriptors = {};
|
|
25
|
+
Object.keys(methods).forEach(key => {
|
|
26
|
+
fullDescriptors[key] = { value: methods[key] };
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const base = defineProperties({}, fullDescriptors);
|
|
28
30
|
|
|
29
31
|
/**
|
|
30
32
|
* Create or inject the eventable instance into the object
|
|
31
33
|
* @param {Object} [o] the optional instance to eventable
|
|
34
|
+
* @param {Object} [options] optional configuration for the emitter
|
|
32
35
|
* @returns o or new Event instance
|
|
33
36
|
*/
|
|
34
|
-
export function wrapEventEmitter(o) {
|
|
37
|
+
export function wrapEventEmitter(o, options) {
|
|
35
38
|
const result = o == null ? create(base) : defineProperties(Object(o), descriptors)
|
|
36
39
|
defineProperty(result, '_events', {})
|
|
40
|
+
if (options && options.emitterOptions) {
|
|
41
|
+
result.setEmitterOptions(options.emitterOptions)
|
|
42
|
+
}
|
|
37
43
|
return result
|
|
38
44
|
};
|
|
39
45
|
|
|
40
|
-
|
|
41
46
|
export default wrapEventEmitter
|
package/docs/.nojekyll
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false.
|