evnty 3.0.40 → 4.0.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/LICENSE +18 -10
- package/README.md +386 -116
- package/build/index.cjs +180 -131
- package/build/index.cjs.map +1 -1
- package/build/index.d.ts +212 -161
- package/build/index.js +174 -128
- package/build/index.js.map +1 -1
- package/package.json +28 -25
- package/src/index.ts +383 -277
package/src/index.ts
CHANGED
|
@@ -34,6 +34,7 @@ export interface Expander<T, R> {
|
|
|
34
34
|
* Removes a listener from the provided array of listeners. It searches for the listener and removes all instances of it from the array.
|
|
35
35
|
* This method ensures that the listener is fully unregistered, preventing any residual calls to a potentially deprecated handler.
|
|
36
36
|
*
|
|
37
|
+
* @internal
|
|
37
38
|
* @param {Listener<T, R>[]} listeners - The array of listeners from which to remove the listener.
|
|
38
39
|
* @param {Listener<T, R>} listener - The listener function to remove from the list of listeners.
|
|
39
40
|
* @returns {boolean} - Returns `true` if the listener was found and removed, `false` otherwise.
|
|
@@ -41,11 +42,12 @@ export interface Expander<T, R> {
|
|
|
41
42
|
* @template T - The type of the event that listeners are associated with.
|
|
42
43
|
* @template R - The type of the return value that listeners are expected to return.
|
|
43
44
|
*
|
|
44
|
-
*
|
|
45
|
+
* ```typescript
|
|
45
46
|
* // Assuming an array of listeners for click events
|
|
46
47
|
* const listeners = [onClickHandler1, onClickHandler2];
|
|
47
48
|
* const wasRemoved = removeListener(listeners, onClickHandler1);
|
|
48
49
|
* console.log(wasRemoved); // Output: true
|
|
50
|
+
* ```
|
|
49
51
|
*/
|
|
50
52
|
export const removeListener = <T, R>(listeners: Listener<T, R>[], listener: Listener<T, R>): boolean => {
|
|
51
53
|
let index = listeners.indexOf(listener);
|
|
@@ -57,13 +59,28 @@ export const removeListener = <T, R>(listeners: Listener<T, R>[], listener: List
|
|
|
57
59
|
return wasRemoved;
|
|
58
60
|
};
|
|
59
61
|
|
|
62
|
+
/**
|
|
63
|
+
* @internal
|
|
64
|
+
* @param timeout
|
|
65
|
+
* @param signal
|
|
66
|
+
* @returns
|
|
67
|
+
*/
|
|
68
|
+
export const setTimeoutAsync = (timeout: number, signal?: AbortSignal) =>
|
|
69
|
+
new Promise<boolean>((resolve) => {
|
|
70
|
+
const timerId = setTimeout(resolve, timeout, true);
|
|
71
|
+
signal?.addEventListener('abort', () => {
|
|
72
|
+
clearTimeout(timerId);
|
|
73
|
+
resolve(false);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
60
77
|
/**
|
|
61
78
|
* An abstract class that extends the built-in Function class. It allows instances of the class
|
|
62
|
-
* to be called as functions. When an instance of
|
|
79
|
+
* to be called as functions. When an instance of Callable is called as a function, it will
|
|
63
80
|
* call the function passed to its constructor with the same arguments.
|
|
64
81
|
* @internal
|
|
65
82
|
*/
|
|
66
|
-
export abstract class
|
|
83
|
+
export abstract class Callable extends Function {
|
|
67
84
|
constructor(func: Function) {
|
|
68
85
|
super();
|
|
69
86
|
return Object.setPrototypeOf(func, new.target.prototype);
|
|
@@ -77,7 +94,7 @@ export interface Unsubscribe {
|
|
|
77
94
|
/**
|
|
78
95
|
* @internal
|
|
79
96
|
*/
|
|
80
|
-
export class Unsubscribe extends
|
|
97
|
+
export class Unsubscribe extends Callable {
|
|
81
98
|
constructor(callback: Callback) {
|
|
82
99
|
super(callback);
|
|
83
100
|
}
|
|
@@ -105,67 +122,28 @@ export class Unsubscribe extends FunctionExt {
|
|
|
105
122
|
}
|
|
106
123
|
}
|
|
107
124
|
|
|
108
|
-
export interface Event<T = any, R = any> {
|
|
109
|
-
(event: T): Promise<(void | Awaited<R>)[]>;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Represents a pair of events handling both successful outcomes and errors.
|
|
114
|
-
* This interface is used to manage asynchronous operations where events can either
|
|
115
|
-
* result in a successful output or an error.
|
|
116
|
-
*
|
|
117
|
-
* The `value` event is triggered when the operation succeeds and emits a result.
|
|
118
|
-
* The `error` event is triggered when the operation encounters an error, allowing
|
|
119
|
-
* error handling mechanisms to process or log the error accordingly.
|
|
120
|
-
*
|
|
121
|
-
* @template T The type of data emitted by the successful outcome of the event.
|
|
122
|
-
* @template R The type of data (if any) emitted by the error event.
|
|
123
|
-
* @template E The type of error information emitted by the error event, usually an Error object or string.
|
|
124
|
-
*
|
|
125
|
-
* @example
|
|
126
|
-
* // Assume an asynchronous function that fetches user data
|
|
127
|
-
* function fetchUserData(): ResultEvents<UserData, Error> {
|
|
128
|
-
* const success = new Event<UserData>();
|
|
129
|
-
* const failure = new Event<Error>();
|
|
130
|
-
*
|
|
131
|
-
* fetch('/api/user')
|
|
132
|
-
* .then(response => response.json())
|
|
133
|
-
* .then(data => success(data))
|
|
134
|
-
* .catch(error => failure(error));
|
|
135
|
-
*
|
|
136
|
-
* return { value: success, error: failure };
|
|
137
|
-
* }
|
|
138
|
-
*
|
|
139
|
-
* const userDataEvent = fetchUserData();
|
|
140
|
-
* userDataEvent.value.on(data => console.log('User data received:', data));
|
|
141
|
-
* userDataEvent.error.on(error => console.error('An error occurred:', error));
|
|
142
|
-
*/
|
|
143
|
-
export interface ResultEvents<T, R, E = unknown> {
|
|
144
|
-
value: Event<T, R>; // Event triggered on a successful result.
|
|
145
|
-
error: Event<E, void>; // Event triggered on an error occurrence.
|
|
146
|
-
}
|
|
147
|
-
|
|
148
125
|
export interface Queue<T> {
|
|
149
|
-
pop(): MaybePromise<T
|
|
126
|
+
pop(): MaybePromise<T>;
|
|
150
127
|
stop(): MaybePromise<void>;
|
|
151
128
|
}
|
|
152
129
|
|
|
130
|
+
export interface Event<T = any, R = any> {
|
|
131
|
+
(event: T): Promise<(void | Awaited<R>)[]>;
|
|
132
|
+
}
|
|
133
|
+
|
|
153
134
|
/**
|
|
154
135
|
* A class representing an anonymous event that can be listened to or triggered.
|
|
155
136
|
*
|
|
156
|
-
* @
|
|
157
|
-
* @
|
|
137
|
+
* @template T - The event type.
|
|
138
|
+
* @template R - The return type of the event.
|
|
158
139
|
*/
|
|
159
|
-
export class Event<T, R> extends
|
|
140
|
+
export class Event<T, R> extends Callable implements AsyncIterable<T>, PromiseLike<T> {
|
|
160
141
|
/**
|
|
161
142
|
* The array of listeners for the event.
|
|
162
143
|
*/
|
|
163
144
|
private listeners: Listener<T, R>[];
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
*/
|
|
167
|
-
private addSpies: Array<(listener: Listener<T, R> | void) => void> = [];
|
|
168
|
-
private removeSpies: Array<(listener: Listener<T, R> | void) => void> = [];
|
|
145
|
+
|
|
146
|
+
private spies: Array<(listener: Listener<T, R> | void) => void> = [];
|
|
169
147
|
|
|
170
148
|
/**
|
|
171
149
|
* A function that disposes of the event and its listeners.
|
|
@@ -174,12 +152,14 @@ export class Event<T, R> extends FunctionExt {
|
|
|
174
152
|
|
|
175
153
|
/**
|
|
176
154
|
* Creates a new event.
|
|
177
|
-
*
|
|
155
|
+
*
|
|
156
|
+
* @param dispose - A function to call on the event disposal.
|
|
157
|
+
*
|
|
158
|
+
* ```typescript
|
|
178
159
|
* // Create a click event.
|
|
179
160
|
* const clickEvent = new Event<[x: number, y: number], void>();
|
|
180
161
|
* clickEvent.on(([x, y]) => console.log(`Clicked at ${x}, ${y}`));
|
|
181
|
-
*
|
|
182
|
-
* @param dispose - A function to call on the event disposal.
|
|
162
|
+
* ```
|
|
183
163
|
*/
|
|
184
164
|
constructor(dispose?: Callback) {
|
|
185
165
|
const listeners: Listener<T, R>[] = [];
|
|
@@ -188,66 +168,81 @@ export class Event<T, R> extends FunctionExt {
|
|
|
188
168
|
this.listeners = listeners;
|
|
189
169
|
|
|
190
170
|
this.dispose = async () => {
|
|
191
|
-
this.clear();
|
|
171
|
+
void this.clear();
|
|
172
|
+
await this._error?.dispose();
|
|
192
173
|
await dispose?.();
|
|
193
174
|
};
|
|
194
175
|
}
|
|
195
176
|
|
|
177
|
+
private _error?: Event<unknown>;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Error event that emits errors.
|
|
181
|
+
*
|
|
182
|
+
* @returns {Event<unknown>} The error event.
|
|
183
|
+
*/
|
|
184
|
+
get error(): Event<unknown> {
|
|
185
|
+
return (this._error ??= new Event<unknown>());
|
|
186
|
+
}
|
|
187
|
+
|
|
196
188
|
/**
|
|
197
189
|
* The number of listeners for the event.
|
|
190
|
+
*
|
|
191
|
+
* @readonly
|
|
192
|
+
* @type {number}
|
|
198
193
|
*/
|
|
199
194
|
get size(): number {
|
|
200
195
|
return this.listeners.length;
|
|
201
196
|
}
|
|
202
197
|
|
|
203
198
|
/**
|
|
204
|
-
* Checks if
|
|
205
|
-
*
|
|
199
|
+
* Checks if the given listener is NOT registered for this event.
|
|
200
|
+
*
|
|
206
201
|
* @param listener - The listener function to check against the registered listeners.
|
|
207
202
|
* @returns `true` if the listener is not already registered; otherwise, `false`.
|
|
208
203
|
*
|
|
209
|
-
*
|
|
204
|
+
* ```typescript
|
|
210
205
|
* // Check if a listener is not already added
|
|
211
206
|
* if (event.lacks(myListener)) {
|
|
212
207
|
* event.on(myListener);
|
|
213
208
|
* }
|
|
214
|
-
*
|
|
209
|
+
* ```
|
|
215
210
|
*/
|
|
216
211
|
lacks(listener: Listener<T, R>): boolean {
|
|
217
212
|
return this.listeners.indexOf(listener) === -1;
|
|
218
213
|
}
|
|
219
214
|
|
|
220
215
|
/**
|
|
221
|
-
* Checks if
|
|
222
|
-
* This method is used to confirm the presence of a listener in the event's registration list.
|
|
216
|
+
* Checks if the given listener is registered for this event.
|
|
223
217
|
*
|
|
224
|
-
* @param listener - The listener function to
|
|
218
|
+
* @param listener - The listener function to check.
|
|
225
219
|
* @returns `true` if the listener is currently registered; otherwise, `false`.
|
|
226
220
|
*
|
|
227
|
-
*
|
|
221
|
+
* ```typescript
|
|
228
222
|
* // Verify if a listener is registered
|
|
229
223
|
* if (event.has(myListener)) {
|
|
230
224
|
* console.log('Listener is already registered');
|
|
231
225
|
* }
|
|
226
|
+
* ```
|
|
232
227
|
*/
|
|
233
228
|
has(listener: Listener<T, R>): boolean {
|
|
234
229
|
return this.listeners.indexOf(listener) !== -1;
|
|
235
230
|
}
|
|
236
231
|
|
|
237
232
|
/**
|
|
238
|
-
* Removes a listener from
|
|
239
|
-
* This method is used when the listener is no longer needed, helping to prevent memory leaks and unnecessary executions.
|
|
233
|
+
* Removes a specific listener from this event.
|
|
240
234
|
*
|
|
241
235
|
* @param listener - The listener to remove.
|
|
242
236
|
* @returns The event instance, allowing for method chaining.
|
|
243
237
|
*
|
|
244
|
-
*
|
|
238
|
+
* ```typescript
|
|
245
239
|
* // Remove a listener
|
|
246
240
|
* event.off(myListener);
|
|
241
|
+
* ```
|
|
247
242
|
*/
|
|
248
243
|
off(listener: Listener<T, R>): this {
|
|
249
|
-
if (removeListener(this.listeners, listener)) {
|
|
250
|
-
this.
|
|
244
|
+
if (removeListener(this.listeners, listener) && this.spies.length) {
|
|
245
|
+
[...this.spies].forEach((spy) => spy(listener));
|
|
251
246
|
}
|
|
252
247
|
return this;
|
|
253
248
|
}
|
|
@@ -259,19 +254,17 @@ export class Event<T, R> extends FunctionExt {
|
|
|
259
254
|
* @param listener - The function to call when the event occurs.
|
|
260
255
|
* @returns An object that can be used to unsubscribe the listener, ensuring easy cleanup.
|
|
261
256
|
*
|
|
262
|
-
*
|
|
257
|
+
* ```typescript
|
|
263
258
|
* // Add a listener to an event
|
|
264
259
|
* const unsubscribe = event.on((data) => {
|
|
265
260
|
* console.log('Event data:', data);
|
|
266
261
|
* });
|
|
262
|
+
* ```
|
|
267
263
|
*/
|
|
268
264
|
on(listener: Listener<T, R>): Unsubscribe {
|
|
269
265
|
this.listeners.push(listener);
|
|
270
|
-
if (this.addSpies.length > 0) {
|
|
271
|
-
this.addSpies.forEach((spy) => spy(listener));
|
|
272
|
-
}
|
|
273
266
|
return new Unsubscribe(() => {
|
|
274
|
-
this.off(listener);
|
|
267
|
+
void this.off(listener);
|
|
275
268
|
});
|
|
276
269
|
}
|
|
277
270
|
|
|
@@ -281,116 +274,252 @@ export class Event<T, R> extends FunctionExt {
|
|
|
281
274
|
*
|
|
282
275
|
* @param listener - The listener to trigger once.
|
|
283
276
|
* @returns An object that can be used to remove the listener if the event has not yet occurred.
|
|
284
|
-
*
|
|
277
|
+
*
|
|
278
|
+
* ```typescript
|
|
285
279
|
* // Register a one-time listener
|
|
286
280
|
* const onceUnsubscribe = event.once((data) => {
|
|
287
281
|
* console.log('Received data once:', data);
|
|
288
282
|
* });
|
|
283
|
+
* ```
|
|
289
284
|
*/
|
|
290
285
|
once(listener: Listener<T, R>): Unsubscribe {
|
|
291
286
|
const oneTimeListener = (event: T) => {
|
|
292
|
-
this.off(oneTimeListener);
|
|
287
|
+
void this.off(oneTimeListener);
|
|
293
288
|
return listener(event);
|
|
294
289
|
};
|
|
295
290
|
return this.on(oneTimeListener);
|
|
296
291
|
}
|
|
297
292
|
|
|
298
|
-
/**
|
|
299
|
-
* Returns a Promise that resolves with the first event argument emitted.
|
|
300
|
-
* This method is useful for scenarios where you need to wait for the first occurrence
|
|
301
|
-
* of an event and then perform actions based on the event data.
|
|
302
|
-
*
|
|
303
|
-
* @returns {Promise<T>} A Promise that resolves with the first event argument emitted.
|
|
304
|
-
* @example
|
|
305
|
-
* const clickEvent = new Event<[number, number]>();
|
|
306
|
-
* clickEvent.onceAsync().then(([x, y]) => {
|
|
307
|
-
* console.log(`First click at (${x}, ${y})`);
|
|
308
|
-
* });
|
|
309
|
-
*/
|
|
310
|
-
onceAsync(): Promise<T> {
|
|
311
|
-
return new Promise<T>((resolve) => this.once((event) => resolve(event)));
|
|
312
|
-
}
|
|
313
|
-
|
|
314
293
|
/**
|
|
315
294
|
* Removes all listeners from the event, effectively resetting it. This is useful when you need to
|
|
316
295
|
* cleanly dispose of all event handlers to prevent memory leaks or unwanted triggerings after certain conditions.
|
|
317
296
|
*
|
|
318
297
|
* @returns {this} The instance of the event, allowing for method chaining.
|
|
319
|
-
*
|
|
298
|
+
*
|
|
299
|
+
* ```typescript
|
|
320
300
|
* const myEvent = new Event();
|
|
321
301
|
* myEvent.on(data => console.log(data));
|
|
322
302
|
* myEvent.clear(); // Clears all listeners
|
|
303
|
+
* ```
|
|
323
304
|
*/
|
|
324
305
|
clear(): this {
|
|
325
306
|
this.listeners.splice(0);
|
|
326
|
-
this.
|
|
307
|
+
if (this.spies.length) {
|
|
308
|
+
[...this.spies].forEach((spy) => spy());
|
|
309
|
+
}
|
|
327
310
|
return this;
|
|
328
311
|
}
|
|
329
312
|
|
|
313
|
+
/**
|
|
314
|
+
* Enables the `Event` to be used in a Promise chain, resolving with the first emitted value.
|
|
315
|
+
*
|
|
316
|
+
* @template TResult1 - The type of the fulfilled value returned by `onfulfilled` (defaults to the event's type).
|
|
317
|
+
* @template TResult2 - The type of the rejected value returned by `onrejected` (defaults to `never`).
|
|
318
|
+
* @param onfulfilled - A function called when the event emits its first value.
|
|
319
|
+
* @param onrejected - A function called if an error occurs before the event emits.
|
|
320
|
+
* @returns A Promise that resolves with the result of `onfulfilled` or `onrejected`.
|
|
321
|
+
*
|
|
322
|
+
* ```typescript
|
|
323
|
+
* const clickEvent = new Event<[number, number]>();
|
|
324
|
+
* await clickEvent;
|
|
325
|
+
* ```
|
|
326
|
+
*/
|
|
327
|
+
then<TResult1 = T, TResult2 = never>(
|
|
328
|
+
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null | undefined,
|
|
329
|
+
onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null | undefined,
|
|
330
|
+
): Promise<TResult1 | TResult2> {
|
|
331
|
+
const promise = new Promise<T>((resolve) => this.once(resolve));
|
|
332
|
+
|
|
333
|
+
return promise.then(onfulfilled, onrejected);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* A promise that resolves with the first emitted value from this event.
|
|
338
|
+
*
|
|
339
|
+
* @returns {Promise<T>} The promise value.
|
|
340
|
+
*/
|
|
341
|
+
get promise(): Promise<T> {
|
|
342
|
+
return this.then((v) => v);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Makes this event iterable using `for await...of` loops.
|
|
347
|
+
*
|
|
348
|
+
* @returns An async iterator that yields values as they are emitted by this event.
|
|
349
|
+
*
|
|
350
|
+
* ```typescript
|
|
351
|
+
* // Assuming an event that emits numbers
|
|
352
|
+
* const numberEvent = new Event<number>();
|
|
353
|
+
* (async () => {
|
|
354
|
+
* for await (const num of numberEvent) {
|
|
355
|
+
* console.log('Number:', num);
|
|
356
|
+
* }
|
|
357
|
+
* })();
|
|
358
|
+
* await numberEvent(1);
|
|
359
|
+
* await numberEvent(2);
|
|
360
|
+
* await numberEvent(3);
|
|
361
|
+
* ```
|
|
362
|
+
*/
|
|
363
|
+
[Symbol.asyncIterator](): AsyncIterator<T> {
|
|
364
|
+
const queue: T[] = [];
|
|
365
|
+
const doneEvent = new Event<boolean>();
|
|
366
|
+
const emitEvent = async (value: T) => {
|
|
367
|
+
queue.push(value);
|
|
368
|
+
await doneEvent(false);
|
|
369
|
+
};
|
|
370
|
+
const unsubscribe = this.on(emitEvent).pre(async () => {
|
|
371
|
+
removeListener(this.spies, spy);
|
|
372
|
+
queue.splice(0);
|
|
373
|
+
await doneEvent.dispose();
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
const spy: (typeof this.spies)[number] = (target = emitEvent) => {
|
|
377
|
+
if (target === emitEvent) {
|
|
378
|
+
void doneEvent(true);
|
|
379
|
+
void unsubscribe();
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
this.spies.push(spy);
|
|
384
|
+
return {
|
|
385
|
+
async next() {
|
|
386
|
+
if (queue.length) {
|
|
387
|
+
return { value: queue.shift()!, done: false };
|
|
388
|
+
}
|
|
389
|
+
if (!(await doneEvent)) {
|
|
390
|
+
return { value: queue.shift()!, done: false };
|
|
391
|
+
}
|
|
392
|
+
return { value: undefined, done: true };
|
|
393
|
+
},
|
|
394
|
+
async return(value: unknown) {
|
|
395
|
+
await unsubscribe();
|
|
396
|
+
return { done: true, value };
|
|
397
|
+
},
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Transforms the event's values using a generator function, creating a new `Event` that emits the transformed values.
|
|
403
|
+
*
|
|
404
|
+
* @template PT - The type of values emitted by the transformed `Event`.
|
|
405
|
+
* @template PR - The return type of the listeners of the transformed `Event`.
|
|
406
|
+
* @param generator - A function that takes the original event's value and returns a generator (sync or async) that yields the transformed values.
|
|
407
|
+
* @returns A new `Event` instance that emits the transformed values.
|
|
408
|
+
*
|
|
409
|
+
* ```typescript
|
|
410
|
+
* const numbersEvent = new Event<number>();
|
|
411
|
+
* const evenNumbersEvent = numbersEvent.pipe(function*(value) {
|
|
412
|
+
* if (value % 2 === 0) {
|
|
413
|
+
* yield value;
|
|
414
|
+
* }
|
|
415
|
+
* });
|
|
416
|
+
* evenNumbersEvent.on((evenNumber) => console.log(evenNumber));
|
|
417
|
+
* await numbersEvent(1);
|
|
418
|
+
* await numbersEvent(2);
|
|
419
|
+
* await numbersEvent(3);
|
|
420
|
+
* ```
|
|
421
|
+
*/
|
|
422
|
+
pipe<PT, R>(generator: (event: T) => AsyncGenerator<PT, void, unknown> | Generator<PT, void, unknown>): Event<PT, R> {
|
|
423
|
+
const emitEvent = async (value: T) => {
|
|
424
|
+
try {
|
|
425
|
+
for await (const generatedValue of generator(value)) {
|
|
426
|
+
await result(generatedValue).catch((e) => result.error(e));
|
|
427
|
+
}
|
|
428
|
+
} catch (e) {
|
|
429
|
+
await result.error(e);
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
const unsubscribe = this.on(emitEvent).pre(() => {
|
|
434
|
+
removeListener(this.spies, spy);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
const spy: (typeof this.spies)[number] = (target = emitEvent) => {
|
|
438
|
+
if (target === emitEvent) {
|
|
439
|
+
void unsubscribe();
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
this.spies.push(spy);
|
|
443
|
+
|
|
444
|
+
const result = new Event<PT, R>(unsubscribe);
|
|
445
|
+
return result;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Creates an async generator that yields values as they are emitted by this event.
|
|
450
|
+
*
|
|
451
|
+
* @template PT - The type of values yielded by the async generator.
|
|
452
|
+
* @param generator - An optional function that takes the original event's value and returns a generator (sync or async)
|
|
453
|
+
* that yields values to include in the async generator.
|
|
454
|
+
* @returns An async generator that yields values from this event as they occur.
|
|
455
|
+
*
|
|
456
|
+
* ```typescript
|
|
457
|
+
* const numbersEvent = new Event<number>();
|
|
458
|
+
* const evenNumbersEvent = numbersEvent.pipe(function*(value) {
|
|
459
|
+
* if (value % 2 === 0) {
|
|
460
|
+
* yield value;
|
|
461
|
+
* }
|
|
462
|
+
* });
|
|
463
|
+
* evenNumbersEvent.on((evenNumber) => console.log(evenNumber));
|
|
464
|
+
* await numbersEvent(1);
|
|
465
|
+
* await numbersEvent(2);
|
|
466
|
+
* await numbersEvent(3);
|
|
467
|
+
* ```
|
|
468
|
+
*/
|
|
469
|
+
async *generator<PT>(generator: (event: T) => AsyncGenerator<PT, void, unknown> | Generator<PT, void, unknown>): AsyncGenerator<Awaited<PT>, void, unknown> {
|
|
470
|
+
for await (const value of this.pipe(generator)) {
|
|
471
|
+
yield value;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
330
475
|
/**
|
|
331
476
|
* Filters events, creating a new event that only triggers when the provided filter function returns `true`.
|
|
332
477
|
* This method can be used to selectively process events that meet certain criteria.
|
|
333
478
|
*
|
|
334
|
-
* @param {Filter<T, P>}
|
|
479
|
+
* @param {Filter<T, P>} predicate The filter function or predicate to apply to each event.
|
|
335
480
|
* @returns {Event<P, R>} A new event that only triggers for filtered events.
|
|
336
|
-
*
|
|
481
|
+
*
|
|
482
|
+
* ```typescript
|
|
337
483
|
* const keyPressedEvent = new Event<string>();
|
|
338
484
|
* const enterPressedEvent = keyPressedEvent.filter(key => key === 'Enter');
|
|
339
485
|
* enterPressedEvent.on(() => console.log('Enter key was pressed.'));
|
|
486
|
+
* ```
|
|
340
487
|
*/
|
|
341
488
|
filter<P extends T>(predicate: Predicate<T, P>): Event<P, R>;
|
|
342
489
|
filter<P extends T>(filter: FilterFunction<T>): Event<P, R>;
|
|
343
490
|
filter<P extends T>(filter: Filter<T, P>): Event<P, R> {
|
|
344
|
-
|
|
345
|
-
if (
|
|
346
|
-
|
|
491
|
+
return this.pipe<P, R>(async function* (value: T) {
|
|
492
|
+
if (await filter(value)) {
|
|
493
|
+
yield value as P;
|
|
347
494
|
}
|
|
348
495
|
});
|
|
349
|
-
const filteredEvent = new Event<P, R>(unsubscribe);
|
|
350
|
-
return filteredEvent;
|
|
351
496
|
}
|
|
352
497
|
|
|
353
498
|
/**
|
|
354
499
|
* Creates a new event that will only be triggered once when the provided filter function returns `true`.
|
|
355
500
|
* This method is useful for handling one-time conditions in a stream of events.
|
|
356
501
|
*
|
|
357
|
-
* @param {Filter<T, P>}
|
|
502
|
+
* @param {Filter<T, P>} predicate - The filter function or predicate.
|
|
358
503
|
* @returns {Event<P, R>} A new event that will be triggered only once when the filter condition is met.
|
|
359
|
-
*
|
|
504
|
+
*
|
|
505
|
+
* ```typescript
|
|
360
506
|
* const sizeChangeEvent = new Event<number>();
|
|
361
507
|
* const sizeReachedEvent = sizeChangeEvent.first(size => size > 1024);
|
|
362
508
|
* sizeReachedEvent.on(() => console.log('Size threshold exceeded.'));
|
|
509
|
+
* ```
|
|
363
510
|
*/
|
|
364
511
|
first<P extends T>(predicate: Predicate<T, P>): Event<P, R>;
|
|
365
512
|
first<P extends T>(filter: FilterFunction<T>): Event<P, R>;
|
|
366
513
|
first<P extends T>(filter: Filter<T, P>): Event<P, R> {
|
|
367
|
-
const
|
|
368
|
-
if (
|
|
369
|
-
|
|
370
|
-
await filteredEvent(
|
|
514
|
+
const filteredEvent = this.pipe<P, R>(async function* (value: T) {
|
|
515
|
+
if (await filter(value)) {
|
|
516
|
+
yield value as P;
|
|
517
|
+
await filteredEvent.dispose();
|
|
371
518
|
}
|
|
372
519
|
});
|
|
373
|
-
const filteredEvent = new Event<P, R>(unsubscribe);
|
|
374
520
|
return filteredEvent;
|
|
375
521
|
}
|
|
376
522
|
|
|
377
|
-
/**
|
|
378
|
-
* Returns a Promise that resolves once an event occurs that meets the filter criteria.
|
|
379
|
-
* This method is particularly useful for handling asynchronous flows where you need to wait for a specific condition.
|
|
380
|
-
*
|
|
381
|
-
* @param {Filter<T, P>} filter - The filter function or predicate.
|
|
382
|
-
* @returns {Promise<P>} A Promise that resolves once the filter condition is met.
|
|
383
|
-
* @example
|
|
384
|
-
* const mouseEvent = new Event<{x: number, y: number}>();
|
|
385
|
-
* const clickAtPosition = mouseEvent.firstAsync(pos => pos.x > 200 && pos.y > 200);
|
|
386
|
-
* clickAtPosition.then(pos => console.log(`Clicked at (${pos.x}, ${pos.y})`));
|
|
387
|
-
*/
|
|
388
|
-
firstAsync<P extends T>(predicate: Predicate<T, P>): Promise<P>;
|
|
389
|
-
firstAsync<P extends T>(filter: FilterFunction<T>): Promise<P>;
|
|
390
|
-
firstAsync<P extends T>(filter: Filter<T, P>): Promise<P> {
|
|
391
|
-
return this.first<P>(filter).onceAsync();
|
|
392
|
-
}
|
|
393
|
-
|
|
394
523
|
/**
|
|
395
524
|
* Transforms the data emitted by this event using a mapping function. Each emitted event is processed by the `mapper`
|
|
396
525
|
* function, which returns a new value that is then emitted by the returned `Event` instance. This is useful for data transformation
|
|
@@ -401,25 +530,18 @@ export class Event<T, R> extends FunctionExt {
|
|
|
401
530
|
* @param {Mapper<T, M>} mapper A function that takes the original event data and returns the transformed data.
|
|
402
531
|
* @returns {Event<M, MR>} A new `Event` instance that emits the mapped values.
|
|
403
532
|
*
|
|
404
|
-
*
|
|
533
|
+
* ```typescript
|
|
405
534
|
* // Assuming an event that emits numbers, create a new event that emits their squares.
|
|
406
535
|
* const numberEvent = new Event<number>();
|
|
407
536
|
* const squaredEvent = numberEvent.map(num => num * num);
|
|
408
537
|
* squaredEvent.on(squared => console.log('Squared number:', squared));
|
|
409
538
|
* await numberEvent(5); // Logs: "Squared number: 25"
|
|
410
|
-
*
|
|
411
|
-
* @param mapper A function that maps the values of this event to a new value.
|
|
412
|
-
* @returns A new event that emits the mapped values.
|
|
539
|
+
* ```
|
|
413
540
|
*/
|
|
414
|
-
map<M, MR = R>(mapper: Mapper<T, M>): Event<M
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
const value = await mapper(event);
|
|
418
|
-
await mappedEvent(value);
|
|
419
|
-
}
|
|
541
|
+
map<M, MR = R>(mapper: Mapper<T, M>): Event<Awaited<M>, MR> {
|
|
542
|
+
return this.pipe(async function* (value) {
|
|
543
|
+
yield await mapper(value);
|
|
420
544
|
});
|
|
421
|
-
const mappedEvent = new Event<M, MR>(unsubscribe);
|
|
422
|
-
return mappedEvent;
|
|
423
545
|
}
|
|
424
546
|
|
|
425
547
|
/**
|
|
@@ -427,30 +549,34 @@ export class Event<T, R> extends FunctionExt {
|
|
|
427
549
|
* function takes the accumulated value and the latest emitted event data, then returns a new accumulated value. This
|
|
428
550
|
* new value is then emitted by the returned `Event` instance. This is particularly useful for accumulating state over time.
|
|
429
551
|
*
|
|
430
|
-
* @example
|
|
431
|
-
* const sumEvent = numberEvent.reduce((a, b) => a + b, 0);
|
|
432
|
-
* sumEvent.on((sum) => console.log(sum)); // 1, 3, 6
|
|
433
|
-
* await sumEvent(1);
|
|
434
|
-
* await sumEvent(2);
|
|
435
|
-
* await sumEvent(3);
|
|
436
|
-
*
|
|
437
552
|
* @template A The type of the accumulator value.
|
|
438
553
|
* @template AR The type of data emitted by the reduced event, usually the same as `A`.
|
|
439
554
|
* @param {Reducer<T, A>} reducer A function that takes the current accumulated value and the new event data, returning the new accumulated value.
|
|
440
555
|
* @param {A} init The initial value of the accumulator.
|
|
441
556
|
* @returns {Event<A, AR>} A new `Event` instance that emits the reduced value.
|
|
442
557
|
*
|
|
558
|
+
* ```typescript
|
|
559
|
+
* const sumEvent = numberEvent.reduce((a, b) => a + b, 0);
|
|
560
|
+
* sumEvent.on((sum) => console.log(sum)); // 1, 3, 6
|
|
561
|
+
* await sumEvent(1);
|
|
562
|
+
* await sumEvent(2);
|
|
563
|
+
* await sumEvent(3);
|
|
564
|
+
* ```
|
|
443
565
|
*/
|
|
444
|
-
reduce<A, AR = R>(reducer: Reducer<T, A>, init
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
566
|
+
reduce<A, AR = R>(reducer: Reducer<T, A>, init?: A): Event<Awaited<A>, AR>;
|
|
567
|
+
reduce<A, AR = R>(reducer: Reducer<T, A>, ...init: unknown[]): Event<Awaited<A>, AR> {
|
|
568
|
+
let hasInit = init.length === 1;
|
|
569
|
+
let result = init[0] as A | undefined;
|
|
570
|
+
|
|
571
|
+
return this.pipe(async function* (value) {
|
|
572
|
+
if (hasInit) {
|
|
573
|
+
result = await reducer(result!, value);
|
|
574
|
+
yield result;
|
|
575
|
+
} else {
|
|
576
|
+
result = value as unknown as A;
|
|
577
|
+
hasInit = true;
|
|
450
578
|
}
|
|
451
579
|
});
|
|
452
|
-
const reducedEvent = new Event<A, AR>(unsubscribe);
|
|
453
|
-
return reducedEvent;
|
|
454
580
|
}
|
|
455
581
|
|
|
456
582
|
/**
|
|
@@ -464,24 +590,21 @@ export class Event<T, R> extends FunctionExt {
|
|
|
464
590
|
* @param {Expander<T, ET[]>} expander - A function that takes the original event data and returns an array of new data elements.
|
|
465
591
|
* @returns {Event<ET, ER>} - A new `Event` instance that emits each value from the array returned by the expander function.
|
|
466
592
|
*
|
|
467
|
-
*
|
|
593
|
+
* ```typescript
|
|
468
594
|
* // Assuming an event that emits a sentence, create a new event that emits each word from the sentence separately.
|
|
469
595
|
* const sentenceEvent = new Event<string>();
|
|
470
596
|
* const wordEvent = sentenceEvent.expand(sentence => sentence.split(' '));
|
|
471
597
|
* wordEvent.on(word => console.log('Word:', word));
|
|
472
598
|
* await sentenceEvent('Hello world'); // Logs: "Word: Hello", "Word: world"
|
|
599
|
+
* ```
|
|
473
600
|
*/
|
|
474
|
-
expand<ET, ER>(expander: Expander<T, ET[]>): Event<ET
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
await expandedEvent(value);
|
|
480
|
-
}
|
|
601
|
+
expand<ET, ER>(expander: Expander<T, ET[]>): Event<Awaited<ET>, ER> {
|
|
602
|
+
return this.pipe(async function* (value) {
|
|
603
|
+
const values = await expander(value);
|
|
604
|
+
for (const value of values) {
|
|
605
|
+
yield value;
|
|
481
606
|
}
|
|
482
607
|
});
|
|
483
|
-
const expandedEvent = new Event<ET, ER>(unsubscribe);
|
|
484
|
-
return expandedEvent;
|
|
485
608
|
}
|
|
486
609
|
|
|
487
610
|
/**
|
|
@@ -489,10 +612,16 @@ export class Event<T, R> extends FunctionExt {
|
|
|
489
612
|
* captured from the original event each time the conductor event is triggered. This method is useful for synchronizing
|
|
490
613
|
* events, where the emission of one event controls the timing of another.
|
|
491
614
|
*
|
|
492
|
-
* @
|
|
615
|
+
* @template T The type of data emitted by the original event.
|
|
616
|
+
* @template R The type of data emitted by the orchestrated event, usually the same as `T`.
|
|
617
|
+
* @param {Event<unknown, unknown>} conductor An event that signals when the orchestrated event should emit.
|
|
618
|
+
* @returns {Event<T, R>} An orchestrated event that emits values based on the conductor's trigger.
|
|
619
|
+
*
|
|
620
|
+
* ```typescript
|
|
493
621
|
* const rightClickPositionEvent = mouseMoveEvent.orchestrate(mouseRightClickEvent);
|
|
622
|
+
* ```
|
|
494
623
|
*
|
|
495
|
-
*
|
|
624
|
+
* ```typescript
|
|
496
625
|
* // An event that emits whenever a "tick" event occurs.
|
|
497
626
|
* const tickEvent = new Event<void>();
|
|
498
627
|
* const dataEvent = new Event<string>();
|
|
@@ -501,12 +630,7 @@ export class Event<T, R> extends FunctionExt {
|
|
|
501
630
|
* await dataEvent('Hello');
|
|
502
631
|
* await dataEvent('World!');
|
|
503
632
|
* await tickEvent(); // Logs: "Data on tick: World!"
|
|
504
|
-
*
|
|
505
|
-
* @template T The type of data emitted by the original event.
|
|
506
|
-
* @template R The type of data emitted by the orchestrated event, usually the same as `T`.
|
|
507
|
-
* @param {Event<unknown, unknown>} conductor An event that signals when the orchestrated event should emit.
|
|
508
|
-
* @returns {Event<T, R>} An orchestrated event that emits values based on the conductor's trigger.
|
|
509
|
-
*
|
|
633
|
+
* ```
|
|
510
634
|
*/
|
|
511
635
|
orchestrate(conductor: Event<any, any>): Event<T, R> {
|
|
512
636
|
let initialized = false;
|
|
@@ -531,29 +655,68 @@ export class Event<T, R> extends FunctionExt {
|
|
|
531
655
|
* at which a function is executed. Common use cases include handling rapid user inputs, window resizing,
|
|
532
656
|
* or scroll events.
|
|
533
657
|
*
|
|
534
|
-
* @
|
|
658
|
+
* @param {number} interval - The amount of time to wait (in milliseconds) before firing the debounced event.
|
|
659
|
+
* @returns {Event<T, R>} An event of debounced events.
|
|
660
|
+
*
|
|
661
|
+
* ```typescript
|
|
535
662
|
* const debouncedEvent = textInputEvent.debounce(100);
|
|
536
663
|
* debouncedEvent.on((str) => console.log(str)); // only 'text' is emitted
|
|
537
664
|
* await event('t');
|
|
538
665
|
* await event('te');
|
|
539
666
|
* await event('tex');
|
|
540
667
|
* await event('text');
|
|
668
|
+
* ```
|
|
669
|
+
*/
|
|
670
|
+
debounce(interval: number): Event<Awaited<T>, unknown> {
|
|
671
|
+
let controller = new AbortController();
|
|
672
|
+
|
|
673
|
+
return this.pipe(async function* (value) {
|
|
674
|
+
controller.abort();
|
|
675
|
+
controller = new AbortController();
|
|
676
|
+
const complete = await setTimeoutAsync(interval, controller.signal);
|
|
677
|
+
if (complete) {
|
|
678
|
+
yield value;
|
|
679
|
+
}
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Creates a throttled event that emits values at most once per specified interval.
|
|
541
685
|
*
|
|
542
|
-
*
|
|
543
|
-
*
|
|
544
|
-
*
|
|
686
|
+
* This is useful for controlling the rate of event emissions, especially for high-frequency events.
|
|
687
|
+
* The throttled event will immediately emit the first value, and then only emit subsequent values
|
|
688
|
+
* if the specified interval has passed since the last emission.
|
|
689
|
+
*
|
|
690
|
+
* @param interval - The time interval (in milliseconds) between allowed emissions.
|
|
691
|
+
* @returns A new Event that emits throttled values.
|
|
692
|
+
*
|
|
693
|
+
* ```typescript
|
|
694
|
+
* const scrollEvent = new Event();
|
|
695
|
+
* const throttledScroll = scrollEvent.throttle(100); // Emit at most every 100ms
|
|
696
|
+
* throttledScroll.on(() => console.log("Throttled scroll event"));
|
|
697
|
+
* ```
|
|
545
698
|
*/
|
|
546
|
-
|
|
547
|
-
let
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
699
|
+
throttle(interval: number): Event<Awaited<T>, unknown> {
|
|
700
|
+
let timeout = 0;
|
|
701
|
+
let pendingValue: T;
|
|
702
|
+
let hasPendingValue = false;
|
|
703
|
+
|
|
704
|
+
return this.pipe(async function* (value) {
|
|
705
|
+
const now = Date.now();
|
|
706
|
+
if (timeout <= now) {
|
|
707
|
+
timeout = now + interval;
|
|
708
|
+
yield value;
|
|
709
|
+
} else {
|
|
710
|
+
pendingValue = value;
|
|
711
|
+
if (!hasPendingValue) {
|
|
712
|
+
hasPendingValue = true;
|
|
713
|
+
await setTimeoutAsync(timeout - now);
|
|
714
|
+
timeout = now + interval;
|
|
715
|
+
hasPendingValue = false;
|
|
716
|
+
yield pendingValue;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
553
719
|
});
|
|
554
|
-
const value = new Event<T, R>(unsubscribe);
|
|
555
|
-
const error = new Event<unknown, void>();
|
|
556
|
-
return { value, error };
|
|
557
720
|
}
|
|
558
721
|
|
|
559
722
|
/**
|
|
@@ -561,98 +724,36 @@ export class Event<T, R> extends FunctionExt {
|
|
|
561
724
|
* time intervals or when the batch reaches a predefined size. This method is useful for grouping
|
|
562
725
|
* a high volume of events into manageable chunks, such as logging or processing data in bulk.
|
|
563
726
|
*
|
|
564
|
-
* @example
|
|
565
|
-
* // Batch messages for bulk processing every 1 second or when 10 messages are collected
|
|
566
|
-
* const messageEvent = createEvent<string, void>();
|
|
567
|
-
* const batchedMessageEvent = messageEvent.batch(1000, 10);
|
|
568
|
-
* batchedMessageEvent.value.on((messages) => console.log('Batched Messages:', messages));
|
|
569
|
-
*
|
|
570
727
|
* @param {number} interval - The time in milliseconds between batch emissions.
|
|
571
728
|
* @param {number} [size] - Optional. The maximum size of each batch. If specified, triggers a batch emission
|
|
572
729
|
* once the batch reaches this size, regardless of the interval.
|
|
573
|
-
* @returns {
|
|
574
|
-
*
|
|
730
|
+
* @returns {Event<T[], R>} An event of the batched results.
|
|
731
|
+
*
|
|
732
|
+
* ```typescript
|
|
733
|
+
* // Batch messages for bulk processing every 1 second or when 10 messages are collected
|
|
734
|
+
* const messageEvent = createEvent<string, void>();
|
|
735
|
+
* const batchedMessageEvent = messageEvent.batch(1000, 10);
|
|
736
|
+
* batchedMessageEvent.on((messages) => console.log('Batched Messages:', messages));
|
|
737
|
+
* ```
|
|
575
738
|
*/
|
|
576
|
-
batch(interval: number, size?: number):
|
|
577
|
-
let
|
|
739
|
+
batch(interval: number, size?: number): Event<T[], R> {
|
|
740
|
+
let controller = new AbortController();
|
|
578
741
|
const batch: T[] = [];
|
|
579
742
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
clearTimeout(timer);
|
|
583
|
-
value(batch.splice(0)).catch(error);
|
|
584
|
-
}
|
|
585
|
-
};
|
|
586
|
-
|
|
587
|
-
const unsubscribe = this.on((event) => {
|
|
588
|
-
if (batch.length === 0) {
|
|
589
|
-
timer = setTimeout(emitBatch, interval);
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
batch.push(event);
|
|
743
|
+
return this.pipe(async function* (value) {
|
|
744
|
+
batch.push(value);
|
|
593
745
|
if (size !== undefined && batch.length >= size) {
|
|
594
|
-
|
|
746
|
+
controller.abort();
|
|
747
|
+
yield batch.splice(0);
|
|
595
748
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
/**
|
|
603
|
-
* Transforms an event into an AsyncIterable that yields values as they are emitted by the event. This allows for the consumption
|
|
604
|
-
* of event data using async iteration mechanisms. The iterator generated will yield all emitted values until the event
|
|
605
|
-
* signals it should no longer be active.
|
|
606
|
-
*
|
|
607
|
-
* @returns {AsyncIterable<T>} An async iterable that yields values emitted by the event.
|
|
608
|
-
* @example
|
|
609
|
-
* // Assuming an event that emits numbers
|
|
610
|
-
* const numberEvent = new Event<number>();
|
|
611
|
-
* const numberIterable = numberEvent.generator();
|
|
612
|
-
* (async () => {
|
|
613
|
-
* for await (const num of numberIterable) {
|
|
614
|
-
* console.log('Number:', num);
|
|
615
|
-
* }
|
|
616
|
-
* })();
|
|
617
|
-
* await numberEvent(1);
|
|
618
|
-
* await numberEvent(2);
|
|
619
|
-
* await numberEvent(3);
|
|
620
|
-
*/
|
|
621
|
-
generator(): AsyncIterable<T> {
|
|
622
|
-
const queue: T[] = [];
|
|
623
|
-
const valueEvent = new Event<boolean>();
|
|
624
|
-
const emitEvent = async (value: T) => {
|
|
625
|
-
queue.push(value);
|
|
626
|
-
await valueEvent(false);
|
|
627
|
-
};
|
|
628
|
-
const unsubscribe = this.on(emitEvent).pre(() => {
|
|
629
|
-
removeListener(this.removeSpies, spy);
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
const spy: (typeof this.removeSpies)[number] = (target = emitEvent) => {
|
|
633
|
-
if (target === emitEvent) {
|
|
634
|
-
valueEvent(true);
|
|
635
|
-
unsubscribe();
|
|
749
|
+
if (batch.length === 1) {
|
|
750
|
+
controller = new AbortController();
|
|
751
|
+
const complete = await setTimeoutAsync(interval, controller.signal);
|
|
752
|
+
if (complete) {
|
|
753
|
+
yield batch.splice(0);
|
|
754
|
+
}
|
|
636
755
|
}
|
|
637
|
-
};
|
|
638
|
-
this.removeSpies.push(spy);
|
|
639
|
-
|
|
640
|
-
return {
|
|
641
|
-
[Symbol.asyncIterator]() {
|
|
642
|
-
return {
|
|
643
|
-
async next() {
|
|
644
|
-
if (queue.length) {
|
|
645
|
-
return Promise.resolve({ value: queue.shift()!, done: false });
|
|
646
|
-
}
|
|
647
|
-
const result = await valueEvent.onceAsync();
|
|
648
|
-
if (!result) {
|
|
649
|
-
return Promise.resolve({ value: queue.shift()!, done: false });
|
|
650
|
-
}
|
|
651
|
-
return Promise.resolve({ value: undefined, done: true });
|
|
652
|
-
},
|
|
653
|
-
};
|
|
654
|
-
},
|
|
655
|
-
};
|
|
756
|
+
});
|
|
656
757
|
}
|
|
657
758
|
|
|
658
759
|
/**
|
|
@@ -660,16 +761,18 @@ export class Event<T, R> extends FunctionExt {
|
|
|
660
761
|
* from the queue, ensuring that elements are handled one at a time. This method is ideal for scenarios where order and sequential processing are critical.
|
|
661
762
|
*
|
|
662
763
|
* @returns {Queue<T>} An object representing the queue. The 'pop' method retrieves the next element from the queue, while 'stop' halts further processing.
|
|
663
|
-
*
|
|
764
|
+
*
|
|
765
|
+
* ```typescript
|
|
664
766
|
* // Queueing tasks for sequential execution
|
|
665
767
|
* const taskEvent = new Event<string>();
|
|
666
768
|
* const taskQueue = taskEvent.queue();
|
|
667
|
-
* await taskEvent('Task 1');
|
|
668
|
-
* await taskEvent('Task 2');
|
|
669
769
|
* (async () => {
|
|
670
770
|
* console.log('Processing:', await taskQueue.pop()); // Processing: Task 1
|
|
671
771
|
* console.log('Processing:', await taskQueue.pop()); // Processing: Task 2
|
|
672
772
|
* })();
|
|
773
|
+
* await taskEvent('Task 1');
|
|
774
|
+
* await taskEvent('Task 2');
|
|
775
|
+
* ```
|
|
673
776
|
*/
|
|
674
777
|
queue(): Queue<T> {
|
|
675
778
|
const queue: T[] = [];
|
|
@@ -682,9 +785,9 @@ export class Event<T, R> extends FunctionExt {
|
|
|
682
785
|
return {
|
|
683
786
|
async pop() {
|
|
684
787
|
if (!queue.length) {
|
|
685
|
-
await valueEvent
|
|
788
|
+
await valueEvent;
|
|
686
789
|
}
|
|
687
|
-
return queue.shift()
|
|
790
|
+
return queue.shift()!;
|
|
688
791
|
},
|
|
689
792
|
async stop() {
|
|
690
793
|
await unsubscribe();
|
|
@@ -712,12 +815,13 @@ export type AllEventsResults<T extends Event<any, any>[]> = { [K in keyof T]: Ev
|
|
|
712
815
|
* @returns {Event<AllEventsParameters<Events>, AllEventsResults<Events>>} - Returns a new `Event` instance
|
|
713
816
|
* that triggers with the parameters and results of any of the merged input events.
|
|
714
817
|
*
|
|
715
|
-
*
|
|
818
|
+
* ```typescript
|
|
716
819
|
* // Merging mouse and keyboard events into a single event
|
|
717
820
|
* const mouseEvent = createEvent<MouseEvent>();
|
|
718
821
|
* const keyboardEvent = createEvent<KeyboardEvent>();
|
|
719
822
|
* const inputEvent = merge(mouseEvent, keyboardEvent);
|
|
720
823
|
* inputEvent.on(event => console.log('Input event:', event));
|
|
824
|
+
* ```
|
|
721
825
|
*/
|
|
722
826
|
export const merge = <Events extends Event<any, any>[]>(...events: Events): Event<AllEventsParameters<Events>, AllEventsResults<Events>> => {
|
|
723
827
|
const mergedEvent = new Event<AllEventsParameters<Events>, AllEventsResults<Events>>();
|
|
@@ -736,10 +840,11 @@ export const merge = <Events extends Event<any, any>[]>(...events: Events): Even
|
|
|
736
840
|
* @returns {Event<number, R>} - An `Event` instance that triggers at the specified interval,
|
|
737
841
|
* emitting an incrementing counter value.
|
|
738
842
|
*
|
|
739
|
-
*
|
|
843
|
+
* ```typescript
|
|
740
844
|
* // Creating an interval event that logs a message every second
|
|
741
845
|
* const tickEvent = createInterval(1000);
|
|
742
846
|
* tickEvent.on(tickNumber => console.log('Tick:', tickNumber));
|
|
847
|
+
* ```
|
|
743
848
|
*/
|
|
744
849
|
export const createInterval = <R = void>(interval: number): Event<number, R> => {
|
|
745
850
|
let counter = 0;
|
|
@@ -756,13 +861,14 @@ export const createInterval = <R = void>(interval: number): Event<number, R> =>
|
|
|
756
861
|
* @typeParam R - The return type of the event handler function, which is emitted after processing the event data.
|
|
757
862
|
* @returns {Event<T, R>} - A new instance of the `Event` class, ready to have listeners added to it.
|
|
758
863
|
*
|
|
759
|
-
*
|
|
864
|
+
* ```typescript
|
|
760
865
|
* // Create a new event that accepts a string and returns the string length
|
|
761
866
|
* const myEvent = createEvent<string, number>();
|
|
762
867
|
* myEvent.on((str: string) => str.length);
|
|
763
868
|
* myEvent('hello').then(results => console.log(results)); // Logs: [5]
|
|
869
|
+
* ```
|
|
764
870
|
*/
|
|
765
|
-
export const createEvent = <T, R =
|
|
871
|
+
export const createEvent = <T = unknown, R = unknown>(): Event<T, R> => new Event<T, R>();
|
|
766
872
|
|
|
767
873
|
export default createEvent;
|
|
768
874
|
|