Skip to content

Commit

Permalink
feat: chainable methods for on, once & off
Browse files Browse the repository at this point in the history
  • Loading branch information
crowlKats committed Dec 5, 2020
1 parent cd5d103 commit c67db31
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 16 deletions.
38 changes: 23 additions & 15 deletions mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,29 @@ export class EventEmitter<E extends Record<string, unknown[]>> {
* No checks are made if the listener was already added, so adding multiple
* listeners will result in the listener being called multiple times.
*/
on<K extends keyof E>(eventName: K, listener: (...args: E[K]) => void): void {
on<K extends keyof E>(
eventName: K,
listener: (...args: E[K]) => void,
): EventEmitter<E> {
if (!this.listeners[eventName]) {
this.listeners[eventName] = [];
}
this.listeners[eventName]!.push({
once: false,
cb: listener,
});
return this;
}

/**
* Returns an asyncIterator which will fire every time eventName is emitted.
*/
async* asyncOn<K extends keyof E>(eventName: K): AsyncIterableIterator<E[K]> {
async *asyncOn<K extends keyof E>(eventName: K): AsyncIterableIterator<E[K]> {
if (!this.#onWriters[eventName]) {
this.#onWriters[eventName] = [];
}

const {readable, writable} = new TransformStream<E[K], E[K]>();
const { readable, writable } = new TransformStream<E[K], E[K]>();
this.#onWriters[eventName]!.push(writable.getWriter());
yield* readable.getIterator();
}
Expand All @@ -50,14 +54,15 @@ export class EventEmitter<E extends Record<string, unknown[]>> {
once<K extends keyof E>(
eventName: K,
listener: (...args: E[K]) => void,
): void {
): EventEmitter<E> {
if (!this.listeners[eventName]) {
this.listeners[eventName] = [];
}
this.listeners[eventName]!.push({
once: true,
cb: listener,
});
return this;
}

/**
Expand All @@ -68,18 +73,19 @@ export class EventEmitter<E extends Record<string, unknown[]>> {
off<K extends keyof E>(
eventName?: K,
listener?: (...args: E[K]) => void,
): void {
): EventEmitter<E> {
if (eventName) {
if (listener) {
this.listeners[eventName] = this.listeners[eventName]?.filter(
({cb}) => cb !== listener,
({ cb }) => cb !== listener,
);
} else {
delete this.listeners[eventName];
}
} else {
this.listeners = Object.create(null);
}
return this;
}

/**
Expand All @@ -89,7 +95,7 @@ export class EventEmitter<E extends Record<string, unknown[]>> {
*/
async emit<K extends keyof E>(eventName: K, ...args: E[K]): Promise<void> {
const listeners = this.listeners[eventName]?.slice() ?? [];
for (const {cb, once} of listeners) {
for (const { cb, once } of listeners) {
cb(...args);

if (once) {
Expand All @@ -110,20 +116,22 @@ export class EventEmitter<E extends Record<string, unknown[]>> {
}
}

async* [Symbol.asyncIterator](): AsyncIterableIterator<{
[K in keyof E]: {
name: K;
value: E[K];
};
}[keyof E]> {
const {readable, writable} = new TransformStream<{
async *[Symbol.asyncIterator](): AsyncIterableIterator<
{
[K in keyof E]: {
name: K;
value: E[K];
};
}[keyof E]
> {
const { readable, writable } = new TransformStream<{
name: keyof E;
value: E[keyof E];
}, {
name: keyof E;
value: E[keyof E];
}>();
this.#writer.push(writable.getWriter());
yield * readable.getIterator();
yield* readable.getIterator();
}
}
14 changes: 13 additions & 1 deletion test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
assertEquals,
fail,
} from "https://deno.land/std@0.76.0/testing/asserts.ts";
} from "https://deno.land/std@0.79.0/testing/asserts.ts";
import { EventEmitter } from "./mod.ts";

type Events = {
Expand Down Expand Up @@ -41,6 +41,18 @@ Deno.test("off", () => {
ee.emit("foo", "bar");
});

Deno.test("chainable", () => {
const ee = new EventEmitter<Events>();

function foo() {
fail();
}

ee.on("foo", foo).off("foo", foo);

ee.emit("foo", "bar");
});

Deno.test("asyncIterator", async () => {
const ee = new EventEmitter<Events>();
setTimeout(() => {
Expand Down

0 comments on commit c67db31

Please sign in to comment.