From bd894584e5fae3562cd0548a602fda5f150b515a Mon Sep 17 00:00:00 2001 From: Lisez Su Date: Sun, 26 May 2024 23:06:31 +0800 Subject: [PATCH] refactor (#16) * refacotr: implement sequence runner * chore: ignore lint dist * refactor: redue lines * refactor: fix import * refactor: detect length * refactor: reduce conjoined event emit --- deno.json | 3 ++- modules/conjoin_emitter.ts | 23 ++++++++++--------- modules/core_emitter.ts | 26 ++++++--------------- modules/emitter.ts | 19 ++++++++-------- modules/helpers.ts | 2 +- modules/runners/sequence.ts | 45 +++++++++++++++++++++++++++++++++++++ modules/types.ts | 1 + modules/xevt.ts | 18 ++++++++------- 8 files changed, 88 insertions(+), 49 deletions(-) create mode 100644 modules/runners/sequence.ts diff --git a/deno.json b/deno.json index ea677ca..c4b1af2 100644 --- a/deno.json +++ b/deno.json @@ -9,7 +9,8 @@ "lint": { "rules": { "exclude": ["no-explicit-any", "no-slow-types"] - } + }, + "exclude": ["dist"] }, "imports": { "@deno/dnt": "jsr:@deno/dnt@^0.41.1", diff --git a/modules/conjoin_emitter.ts b/modules/conjoin_emitter.ts index c19a00d..52fd412 100644 --- a/modules/conjoin_emitter.ts +++ b/modules/conjoin_emitter.ts @@ -7,10 +7,11 @@ import type { EventOptions, PendingConjoinEvent, XConjoinEmitter, -} from "./types.ts"; +} from "modules/types.ts"; -import { CoreEmitter } from "./core_emitter.ts"; -import { Emitter } from "./emitter.ts"; +import { CoreEmitter } from "modules/core_emitter.ts"; +import { Emitter } from "modules/emitter.ts"; +import { SequenceRunner } from "modules/runners/sequence.ts"; export class ConjoinEmitter extends CoreEmitter implements XConjoinEmitter { @@ -23,6 +24,10 @@ export class ConjoinEmitter extends CoreEmitter private prevEvents?: Promise; debug = false; + hasEvent(event: EventName): boolean { + return this.nameIndex.has(event); + } + private internalConjoinOn(signature: EventHandlerSignature) { if (signature.name.length < 2) { throw new RangeError("Conjoin events must have at least two events"); @@ -118,15 +123,13 @@ export class ConjoinEmitter extends CoreEmitter if (!event) return; const handlers = this.handlers.get(event)?.slice() || []; - handlers - .filter((e) => e.options?.once) - .forEach((e) => { - this.offByHandler(event, e.handler); - }); + for (const e of handlers.filter((e) => e.options?.once)) { + this.offByHandler(event, e.handler); + } try { if (handlers.length) { - const result = this.internalExec(0, handlers); + const result = new SequenceRunner(handlers).exec(0); if (result) { return result.then(() => this.exec(pointer + 1, events)); } @@ -139,7 +142,7 @@ export class ConjoinEmitter extends CoreEmitter emit(event: EventName): any { if (this.debug) this.logger.debug("emit", event); - if (!this.nameIndex.has(event)) return; + if (!this.hasEvent(event)) return; let executing: EventName[] = []; let nextIdle: PendingConjoinEvent[] = []; diff --git a/modules/core_emitter.ts b/modules/core_emitter.ts index 7a0639f..df87f7b 100644 --- a/modules/core_emitter.ts +++ b/modules/core_emitter.ts @@ -6,9 +6,9 @@ import type { EventUnscriber, RegisteredHandlers, XCoreEmitter, -} from "./types.ts"; +} from "modules/types.ts"; -import { Logger } from "./logger.ts"; +import { Logger } from "modules/logger.ts"; export abstract class CoreEmitter implements XCoreEmitter { protected handlers: RegisteredHandlers; @@ -24,24 +24,12 @@ export abstract class CoreEmitter implements XCoreEmitter { return Array.from(this.handlers.keys()).flat(); } - abstract emit(event: EventName, ...args: any[]): void; - - protected internalExec( - pointer: number, - signatures: EventHandlerSignature[], - ...args: any[] - ): any { - const profile = signatures[pointer]; - if (!profile) return; - if (profile.options?.async) { - return profile - .handler(...args) - .then(() => this.internalExec(pointer + 1, signatures, ...args)); - } - profile.handler(...args); - return this.internalExec(pointer + 1, signatures, ...args); + hasEvent(event: EventName): boolean { + return !!this.handlers.has(event); } + abstract emit(event: EventName, ...args: any[]): void; + protected onBySignature( name: EventName, signature: EventHandlerSignature, @@ -75,7 +63,7 @@ export abstract class CoreEmitter implements XCoreEmitter { protected offByHandler(event: EventName, handler: EventHandler): void { const handlers = this.handlers.get(event); - if (!handlers) return; + if (!handlers?.length) return; const idx = handlers.findIndex((h) => h.handler === handler); if (idx !== -1) handlers.splice(idx, 1); } diff --git a/modules/emitter.ts b/modules/emitter.ts index 0c34b09..1ff8387 100644 --- a/modules/emitter.ts +++ b/modules/emitter.ts @@ -4,14 +4,15 @@ import type { EventName, EventOptions, XevtEmitter, -} from "./types.ts"; +} from "modules/types.ts"; -import { CoreEmitter } from "./core_emitter.ts"; +import { CoreEmitter } from "modules/core_emitter.ts"; +import { SequenceRunner } from "modules/runners/sequence.ts"; export const EmitDone = Symbol("emit_done"); export class Emitter extends CoreEmitter implements XevtEmitter { - private prevEvents?: Promise; + private prevEvents?: Promise | void; debug = false; on(event: EventName, handler: EventHandler, options?: Partial) { @@ -53,19 +54,17 @@ export class Emitter extends CoreEmitter implements XevtEmitter { if (this.debug) this.logger.debug("emit", event, args); const handlers = this.handlers.get(event)?.slice() || []; - handlers - .filter((e) => e.options?.once) - .forEach((e) => { - this.offByHandler(event, e.handler); - }); + for (const e of handlers.filter((e) => e.options?.once)) { + this.offByHandler(event, e.handler); + } try { if (this.prevEvents) { this.prevEvents = this.prevEvents.then(() => - this.internalExec(0, handlers, ...args) + new SequenceRunner(handlers).exec(0, ...args) ); } else { - this.prevEvents = this.internalExec(0, handlers, ...args); + this.prevEvents = new SequenceRunner(handlers).exec(0, ...args); } return this.prevEvents; } catch (err) { diff --git a/modules/helpers.ts b/modules/helpers.ts index 46a2c7c..98a0ae1 100644 --- a/modules/helpers.ts +++ b/modules/helpers.ts @@ -1,4 +1,4 @@ -import type { ConjoinEvents, EventName } from "./types.ts"; +import type { ConjoinEvents, EventName } from "modules/types.ts"; export function isConjoinEvents( event: EventName | EventName[] | ConjoinEvents, diff --git a/modules/runners/sequence.ts b/modules/runners/sequence.ts new file mode 100644 index 0000000..14eb938 --- /dev/null +++ b/modules/runners/sequence.ts @@ -0,0 +1,45 @@ +import type { EventHandlerSignature } from "modules/types.ts"; + +/** + * Run handlers in sequence. + */ +export class SequenceRunner { + /** + * Create a new instance of the SequenceRunner. + * @param handlers The handlers to run. + */ + constructor(private handlers: EventHandlerSignature[]) { + this.handlers = handlers; + } + + /** + * Wait for the handler to finish before moving to the next handler. + * @param pointer The current handler index. + * @param profile The handler profile. + * @param args The arguments to pass to the handlers. + */ + private asyncExec( + pointer: number, + profile: EventHandlerSignature, + ...args: any[] + ): Promise { + return Promise.resolve(profile.handler(...args)).then(() => + this.exec(pointer + 1, ...args) + ); + } + + /** + * Execute the handlers in sequence. + * @param pointer The current handler index. + * @param args The arguments to pass to the handlers. + */ + exec(pointer: number = 0, ...args: any[]): void | Promise { + const profile = this.handlers[pointer]; + if (!profile) return; + if (profile.options?.async) { + return this.asyncExec(pointer, profile, ...args); + } + profile.handler(...args); + return this.exec(pointer + 1, ...args); + } +} diff --git a/modules/types.ts b/modules/types.ts index bdcd910..b28229a 100644 --- a/modules/types.ts +++ b/modules/types.ts @@ -58,6 +58,7 @@ export type XCoreEmitter = debug: boolean; logger: Pick; eventNames(): EventName[]; + hasEvent(event: EventName): boolean; emit(event: EventName, ...args: any[]): void; error(handler: ErrorHandler): void; } diff --git a/modules/xevt.ts b/modules/xevt.ts index a94863b..e48da98 100644 --- a/modules/xevt.ts +++ b/modules/xevt.ts @@ -7,12 +7,12 @@ import type { RegisteredHandlers, XConjoinEmitter, XevtEmitter, -} from "./types.ts"; +} from "modules/types.ts"; -import { CoreEmitter } from "./core_emitter.ts"; -import { isConjoinEvents } from "./helpers.ts"; -import { ConjoinEmitter } from "./conjoin_emitter.ts"; -import { EmitDone, Emitter } from "./emitter.ts"; +import { CoreEmitter } from "modules/core_emitter.ts"; +import { isConjoinEvents } from "modules/helpers.ts"; +import { ConjoinEmitter } from "modules/conjoin_emitter.ts"; +import { EmitDone, Emitter } from "modules/emitter.ts"; export type XeventName = EventName | ConjoinEvents; @@ -73,9 +73,11 @@ export class Xevt extends CoreEmitter } emit(event: EventName, ...args: any[]): void { - this.emitter.on(EmitDone, () => { - this.conjoinEmitter.emit(event); - }); + if (this.conjoinEmitter.hasEvent(event)) { + this.emitter.on(EmitDone, () => { + this.conjoinEmitter.emit(event); + }); + } this.emitter.emit(event, ...args); }