diff --git a/mod.ts b/mod.ts index d397aa6..c4bde87 100644 --- a/mod.ts +++ b/mod.ts @@ -9,7 +9,7 @@ export class EventEmitter> { #listeners: { [K in keyof E]?: Array<{ once: boolean; - cb: (...args: E[K]) => void; + cb: (...args: E[K]) => void | Promise; }>; } = {}; #globalWriters: WritableStreamDefaultWriter>[] = []; @@ -183,6 +183,26 @@ export class EventEmitter> { } } + /** + * Call and, if async, await the completion of the listeners registered for + * the event named eventName, in the order they were registered, passing the + * supplied arguments to each. Returns a Promise that resolves on the completion + * of the last listener. + * + * Does not support being awaited the same was as `emit`. + */ + async wait(eventName: K, ...args: E[K]): Promise { + const listeners = this.#listeners[eventName]?.slice() ?? []; + for (const { cb, once } of listeners) { + const ret = cb(...args); + if (ret) await ret; + + if (once) { + this.off(eventName, cb); + } + } + } + [Symbol.asyncIterator](): AsyncIterableIterator< { [V in K]: Entry }[K] > { diff --git a/test.ts b/test.ts index 5bcbb18..708f9f7 100644 --- a/test.ts +++ b/test.ts @@ -171,7 +171,7 @@ Deno.test("closeMixed", async () => { } })(); - for await (const x of ee) { + for await (const _ of ee) { await ee.off(); } }); @@ -182,3 +182,17 @@ Deno.test("limitReached", () => { ee.on("foo", () => {}); assertThrows(() => ee.on("foo", () => {})); }); + +Deno.test("wait", async () => { + const ee = new EventEmitter(); + + ee.on("foo", async () => { + console.log("foo"); + await new Promise((resolve) => setTimeout(resolve, 1000)); + }).on("bar", () => { + console.log("bar"); + }); + + await ee.wait("foo", "baz"); + await ee.wait("bar", 0); +});