UNPKG

metaapi.cloud-sdk

Version:

SDK for MetaApi, a professional cloud forex API which includes MetaTrader REST API and MetaTrader websocket API. Supports both MetaTrader 5 (MT5) and MetaTrader 4 (MT4). CopyFactory copy trading API included. (https://metaapi.cloud)

150 lines (131 loc) 4.19 kB
'use strict'; import LoggerManager from '../logger'; /** * Custom event emitter implementation for use */ class EventEmitter<L extends ListenerSignature<L> = DefaultListener> { private _listeners: {[E in keyof L]?: Set<L[E]>}; private _eventEmitterLogger = LoggerManager.getLogger('EventEmitter'); /** * Constructs instance */ constructor() { this._listeners = {}; } /** * Subscribes a listener to event * @param event Event to subscribe to * @param callback Listener function to subscribe */ on<U extends EventEmitter.Event<L>>(event: U, callback: L[U]) { if (!event) { throw new Error('Event name is empty or undefined'); } this._listeners[event] = this._listeners[event] || new Set(); this._listeners[event].add(callback); } /** * Unsubscribes a listener from event * @param event Event to unsubscribe from * @param callback Callback to unsubscribe */ off<U extends EventEmitter.Event<L>>(event: U, callback: L[U]) { if (this._listeners[event]) { this._listeners[event].delete(callback); if (!this._listeners[event].size) { delete this._listeners[event]; } } } /** * Subscribes a listener to event once * @param event Event to subscribe to * @param callback Listener function to subscribe * @param options Additional options */ once<U extends EventEmitter.Event<L>>(event: U, callback: L[U], options?: EventEmitter.OnceOptions<L[U]>) { let listener: any = ((...args: Parameters<L[U]>) => { if (!options?.ifArgs || options?.ifArgs(...args)) { this.off(event, listener); return callback(...args); } }); this.on(event, listener); } /** * Emits an event * @param event Event to emit * @param data Event payload * @returns Promise resolving when all listeners calls completed. Usually async version is useful if listeners may * return promises and the call should wait for them. If one of listener rejects or throws an error, it will be * logged. Async listeners called all at once */ async emit<U extends EventEmitter.Event<L>>(event: U, ...args: Parameters<L[U]>): Promise<void> { if (!this._listeners[event]?.size) { return; } let results = []; for (let listener of this._listeners[event]) { results.push(this._callListener(event as string, listener, args)); } return Promise.all(results) as any; } /** * Returns subscribed events * @returns subscribed events */ getSubscriptions(): EventEmitter.Event<L>[] { return Object.keys(this._listeners) as EventEmitter.Event<L>[]; } /** * Returns subscribed event listeners * @param event Event * @returns Listeners */ getListeners<U extends EventEmitter.Event<L>>(event: U): L[U][] { if (!this._listeners[event]?.size) { return []; } return [...this._listeners[event].values()]; } /** * Returns whether has listeners on specific event * @param event Event * @returns Whether has listeners */ hasListeners<U extends EventEmitter.Event<L>>(event: U): boolean { return !!this._listeners[event]; } private async _callListener(event: string, listener: (...args: any) => any, args: any[]) { try { await listener(...args); } catch (err) { this._eventEmitterLogger.error(`${event}: listener failed with error`, err); } } } namespace EventEmitter { /** Method options */ export type OnceOptions<Listener extends (...args: any[]) => any> = { /** * If specified, the listener will be called only if the callback returns true. Otherwise, the listener will wait * for the next matching call */ ifArgs?: (...args: Parameters<Listener>) => boolean; }; /** Infers event type of a listener */ export type Event<L extends ListenerSignature<L>> = Extract<keyof L, string>; } export default EventEmitter; /** * Event emitter listener type */ export type ListenerSignature<Listeners> = { [EventName in keyof Listeners]: (...args: any[]) => any; }; /** * Default event emitter listener type */ export type DefaultListener = { [k: string]: (...args: any[]) => any; };