@wonderlandengine/api
Version:
Wonderland Engine's JavaScript API.
344 lines (343 loc) • 10.8 kB
TypeScript
/**
* Listener callback type, used in {@link Emitter.add}.
*/
export type ListenerCallback<T extends unknown[] = void[]> = (...data: T) => void;
/**
* Registration options for a listener in an {@link Emitter}.
*/
export interface ListenerOptions {
/**
* Listener identifier. This is used to find and remove the listener
* without needing the callback reference.
*
* The identifier can be any type. However, remember that the comparison will be
* by-value for primitive types (string, number), but by reference for objects.
*
* For more information, please look at the {@link Emitter.remove} method.
*/
id: any | undefined;
/**
* If `true`, the listener is automatically removed after it's invoked.
* Defaults to `false`.
*/
once: boolean;
}
/** Internal listener type. */
type Listener<T extends unknown[]> = ListenerOptions & {
callback: ListenerCallback<T>;
};
/**
* Event emitter.
*
* This class allows to register listeners that will get notified by the emitter.
*
* Usage example:
*
* ```js
* // `onPreRender` is an `Emitter` instance.
* scene.onPreRender.add(() => console.log('before rendering'));
* // `onPostRender` is an `Emitter` instance.
* scene.onPostRender.add(() => console.log('after rendering'));
* ```
*
* You can create your own emitters:
*
* ```js
* import {Emitter} from '@wonderlandengine/api';
*
* const emitter = new Emitter();
* ```
*
* You can notify listeners in to your emitter using {@link Emitter.notify}:
*
* ```js
* // Notifies all the listeners.
* emitter.notify();
* // Notifies all the listeners with some data.
* emitter.notify({ myInt: 42, myStr: 'Hello World!' });
* ```
*
* @category event
*/
export declare class Emitter<T extends unknown[] = void[]> {
/**
* List of listeners to trigger when `notify` is called.
*
* @hidden
*/
protected readonly _listeners: Listener<T>[];
/**
* `true` if the emitter is currently notifying listeners. This
* is used to defer addition and removal.
*
* @hidden
*/
private _notifying;
/**
* Pending additions / removals, performed during a notification.
*
* @hidden
*/
private readonly _transactions;
/**
* Register a new listener to be triggered on {@link Emitter.notify}.
*
* Basic usage:
*
* ```js
* emitter.add((data) => {
* console.log('event received!');
* console.log(data);
* });
* ```
*
* Automatically remove the listener when an event is received:
*
* ```js
* emitter.add((data) => {
* console.log('event received!');
* console.log(data);
* }, {once: true});
* ```
*
* @param listener The callback to register.
* @param opts The listener options. For more information, please have a look
* at the {@link ListenerOptions} interface.
*
* @returns Reference to self (for method chaining)
*/
add(listener: ListenerCallback<T>, opts?: Partial<ListenerOptions>): this;
/**
* Equivalent to {@link Emitter.add}.
*
* @param listeners The callback(s) to register.
* @returns Reference to self (for method chaining).
*
* @deprecated Please use {@link Emitter.add} instead.
*/
push(...listeners: ListenerCallback<T>[]): this;
/**
* Register a new listener to be triggered on {@link Emitter.notify}.
*
* Once notified, the listener will be automatically removed.
*
* The method is equivalent to calling {@link Emitter.add} with:
*
* ```js
* emitter.add(listener, {once: true});
* ```
*
* @param listener The callback to register.
*
* @returns Reference to self (for method chaining).
*/
once(listener: ListenerCallback<T>): this;
/**
* Remove a registered listener.
*
* Usage with a callback:
*
* ```js
* const listener = (data) => console.log(data);
* emitter.add(listener);
*
* // Remove using the callback reference:
* emitter.remove(listener);
* ```
*
* Usage with an id:
*
* ```js
* emitter.add((data) => console.log(data), {id: 'my-callback'});
*
* // Remove using the id:
* emitter.remove('my-callback');
* ```
*
* Using identifiers, you will need to ensure your value is unique to avoid
* removing listeners from other libraries, e.g.,:
*
* ```js
* emitter.add((data) => console.log(data), {id: 'non-unique'});
* // This second listener could be added by a third-party library.
* emitter.add((data) => console.log('Hello From Library!'), {id: 'non-unique'});
*
* // Ho Snap! This also removed the library listener!
* emitter.remove('non-unique');
* ```
*
* The identifier can be any type. However, remember that the comparison will be
* by-value for primitive types (string, number), but by reference for objects.
*
* Example:
*
* ```js
* emitter.add(() => console.log('Hello'), {id: {value: 42}});
* emitter.add(() => console.log('World!'), {id: {value: 42}});
* emitter.remove({value: 42}); // None of the above listeners match!
* emitter.notify(); // Prints 'Hello' and 'World!'.
* ```
*
* Here, both emitters have id `{value: 42}`, but the comparison is made by reference. Thus,
* the `remove()` call has no effect. We can make it work by doing:
*
* ```js
* const id = {value: 42};
* emitter.add(() => console.log('Hello'), {id});
* emitter.add(() => console.log('World!'), {id});
* emitter.remove(id); // Same reference, it works!
* emitter.notify(); // Doesn't print.
* ```
*
* @param listener The registered callback or a value representing the `id`.
*
* @returns Reference to self (for method chaining)
*/
remove(listener: ListenerCallback<T> | any): this;
/**
* Check whether the listener is registered.
*
* @note This method performs a linear search.
*
* * @note Doesn't account for pending listeners, i.e.,
* listeners added / removed during a notification.
*
* @param listener The registered callback or a value representing the `id`.
* @returns `true` if the handle is found, `false` otherwise.
*/
has(listener: ListenerCallback<T> | any): boolean;
/**
* Notify listeners with the given data object.
*
* @note This method ensures all listeners are called even if
* an exception is thrown. For (possibly) faster notification,
* please use {@link Emitter.notifyUnsafe}.
*
* @param data The data to pass to listener when invoked.
*/
notify(...data: T): void;
/**
* Notify listeners with the given data object.
*
* @note Because this method doesn't catch exceptions, some listeners
* will be skipped on a throw. Please use {@link Emitter.notify} for safe
* notification.
*
* @param data The data to pass to listener when invoked.
*/
notifyUnsafe(...data: T): void;
/**
* Return a promise that will resolve on the next event.
*
* @note The promise might never resolve if no event is sent.
*
* @returns A promise that resolves with the data passed to
* {@link Emitter.notify}.
*/
promise(): Promise<T>;
/**
* Number of listeners.
*
* @note Doesn't account for pending listeners, i.e.,
* listeners added / removed during a notification.
*/
get listenerCount(): number;
/** `true` if it has no listeners, `false` otherwise. */
get isEmpty(): boolean;
/**
* Flush all pending transactions.
*
* @hidden
*/
private _flushTransactions;
}
/**
* Registration options for a listener in an {@link RetainEmitter}.
*
* Those options extend {@link ListenerOptions}.
*/
export interface RetainListenerOptions extends ListenerOptions {
/**
* If `true`, directly resolves if the emitter retains a value. If `false`,
* the listener isn't invoked until the next {@link Emitter.notify}.
*
* Defaults to `true`.
*/
immediate: boolean;
}
declare const RetainEmitterUndefined: Record<any, unknown>;
/**
* Event emitter that retains event data when notified.
*
* After a notification happens, subsequent calls to {@link RetainEmitter.add} will get
* automatically notified.
*
* You can use another emitter in order to cancel the last retained event:
*
* ```js
* import {Emitter, RetainedEmitter} from '@wonderlandengine/api';
*
* const onStart = new RetainedEmitter();
*
* onStart.notify(42);
* onStart.add((data) => console.log(data)) // Prints '42'.
* ```
*
* You can reset the state of the emitter, i.e., making it forget about the
* last event using:
*
* ```js
* import {Emitter, RetainedEmitter} from '@wonderlandengine/api';
*
* const onStart = new RetainedEmitter();
* onStart.notify(42);
* onStart.add((data) => console.log(data)) // Prints '42'.
*
* // Reset the state of the emitter.
* onStart.reset();
* onStart.add((data) => console.log(data)) // Doesn't print anything.
* ```
*
* For more information about emitters, please have a look at the base {@link Emitter} class.
*
* @category event
*/
export declare class RetainEmitter<T extends unknown[] = void[]> extends Emitter<T> {
/** Pre-resolved data. @hidden */
protected _event: T | typeof RetainEmitterUndefined;
/**
* Emitter target used to reset the state of this emitter.
*
* @hidden
*/
protected readonly _reset: Emitter<any> | undefined;
/** @override */
add(listener: ListenerCallback<T>, opts?: Partial<RetainListenerOptions>): this;
/**
* @override
*
* @param listener The callback to register.
* @param immediate If `true`, directly resolves if the emitter retains a value.
*
* @returns Reference to self (for method chaining).
*/
once(listener: ListenerCallback<T>, immediate?: boolean): this;
/** @override */
notify(...data: T): void;
/** @override */
notifyUnsafe(...data: T): void;
/**
* Reset the state of the emitter.
*
* Further call to {@link Emitter.add} will not automatically resolve,
* until a new call to {@link Emitter.notify} is performed.
*
* @returns Reference to self (for method chaining)
*/
reset(): this;
/** Returns the retained data, or `undefined` if no data was retained. */
get data(): T | undefined;
/** `true` if data is retained from the last event, `false` otherwise. */
get isDataRetained(): boolean;
}
export {};