Skip to content

Commit

Permalink
refactor (#16)
Browse files Browse the repository at this point in the history
* refacotr: implement sequence runner

* chore: ignore lint dist

* refactor: redue lines

* refactor: fix import

* refactor: detect length

* refactor: reduce conjoined event emit
  • Loading branch information
lisez authored May 26, 2024
1 parent 6393cf7 commit bd89458
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 49 deletions.
3 changes: 2 additions & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"lint": {
"rules": {
"exclude": ["no-explicit-any", "no-slow-types"]
}
},
"exclude": ["dist"]
},
"imports": {
"@deno/dnt": "jsr:@deno/dnt@^0.41.1",
Expand Down
23 changes: 13 additions & 10 deletions modules/conjoin_emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<ConjoinEvents>
implements XConjoinEmitter {
Expand All @@ -23,6 +24,10 @@ export class ConjoinEmitter extends CoreEmitter<ConjoinEvents>
private prevEvents?: Promise<any>;
debug = false;

hasEvent(event: EventName): boolean {
return this.nameIndex.has(event);
}

private internalConjoinOn(signature: EventHandlerSignature<ConjoinEvents>) {
if (signature.name.length < 2) {
throw new RangeError("Conjoin events must have at least two events");
Expand Down Expand Up @@ -118,15 +123,13 @@ export class ConjoinEmitter extends CoreEmitter<ConjoinEvents>
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));
}
Expand All @@ -139,7 +142,7 @@ export class ConjoinEmitter extends CoreEmitter<ConjoinEvents>

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[] = [];
Expand Down
26 changes: 7 additions & 19 deletions modules/core_emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> implements XCoreEmitter<T> {
protected handlers: RegisteredHandlers;
Expand All @@ -24,24 +24,12 @@ export abstract class CoreEmitter<T> implements XCoreEmitter<T> {
return Array.from(this.handlers.keys()).flat();
}

abstract emit(event: EventName, ...args: any[]): void;

protected internalExec(
pointer: number,
signatures: EventHandlerSignature<any>[],
...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<any>,
Expand Down Expand Up @@ -75,7 +63,7 @@ export abstract class CoreEmitter<T> implements XCoreEmitter<T> {

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);
}
Expand Down
19 changes: 9 additions & 10 deletions modules/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<EventName> implements XevtEmitter {
private prevEvents?: Promise<any>;
private prevEvents?: Promise<void> | void;
debug = false;

on(event: EventName, handler: EventHandler, options?: Partial<EventOptions>) {
Expand Down Expand Up @@ -53,19 +54,17 @@ export class Emitter extends CoreEmitter<EventName> 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) {
Expand Down
2 changes: 1 addition & 1 deletion modules/helpers.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
45 changes: 45 additions & 0 deletions modules/runners/sequence.ts
Original file line number Diff line number Diff line change
@@ -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<any>[]) {
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<any>,
...args: any[]
): Promise<void> {
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<void> {
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);
}
}
1 change: 1 addition & 0 deletions modules/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export type XCoreEmitter<T> =
debug: boolean;
logger: Pick<Console, "debug">;
eventNames(): EventName[];
hasEvent(event: EventName): boolean;
emit(event: EventName, ...args: any[]): void;
error(handler: ErrorHandler): void;
}
Expand Down
18 changes: 10 additions & 8 deletions modules/xevt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -73,9 +73,11 @@ export class Xevt extends CoreEmitter<XeventName>
}

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);
}

Expand Down

0 comments on commit bd89458

Please sign in to comment.