From 9f83f87480caa99bbb807200b3f55eb1f27a8789 Mon Sep 17 00:00:00 2001 From: futpib Date: Sat, 6 Aug 2022 02:42:27 +0400 Subject: [PATCH] Add an `off` method to promise returned from `once` --- index.d.ts | 11 +++++++++-- index.js | 11 ++++++++--- index.test-d.ts | 3 +++ test/index.js | 24 ++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/index.d.ts b/index.d.ts index de19515..9429799 100644 --- a/index.d.ts +++ b/index.d.ts @@ -132,6 +132,13 @@ interface Options { debug?: DebugOptions; } +/** +A promise returned from `emittery.once` with an extra `off` method to cancel your subscription. +*/ +interface EmitteryOncePromise extends Promise { + off(): void; +} + /** Emittery is a strictly typed, fully async EventEmitter implementation. Event listeners can be registered with `on` or `once`, and events can be emitted with `emit`. @@ -436,7 +443,7 @@ declare class Emittery< Subscribe to one or more events only once. It will be unsubscribed after the first event. - @returns The event data when `eventName` is emitted. + @returns The promise of event data when `eventName` is emitted. This promise is extended with an `off` method. @example ``` @@ -457,7 +464,7 @@ declare class Emittery< emitter.emit('🐶', '🍖'); // Nothing happens ``` */ - once(eventName: Name | Name[]): Promise; + once(eventName: Name | Name[]): EmitteryOncePromise; /** Trigger an event asynchronously, optionally with some data. Listeners are called in the order they were added, but executed concurrently. diff --git a/index.js b/index.js index 09c2fcc..e8eaabb 100644 --- a/index.js +++ b/index.js @@ -283,12 +283,17 @@ class Emittery { } once(eventNames) { - return new Promise(resolve => { - const off = this.on(eventNames, data => { - off(); + let off_; + + const promise = new Promise(resolve => { + off_ = this.on(eventNames, data => { + off_(); resolve(data); }); }); + + promise.off = off_; + return promise; } events(eventNames) { diff --git a/index.test-d.ts b/index.test-d.ts index 8fb41a2..2bd77fa 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -54,6 +54,9 @@ type AnyListener = (eventData?: unknown) => void | Promise; expectType(eventName); expectType(listener); }); + const oncePromise = ee.once('anotherEvent'); + oncePromise.off(); + await oncePromise; }; } diff --git a/test/index.js b/test/index.js index 566c47d..7de7788 100644 --- a/test/index.js +++ b/test/index.js @@ -374,6 +374,30 @@ test('once() - eventName must be a string, symbol, or number', async t => { await t.throwsAsync(emitter.once(true), TypeError); }); +test('once() - returns a promise with an unsubscribe method', async t => { + const fixture = '🌈'; + const emitter = new Emittery(); + const oncePromise = emitter.once('🦄'); + + const testFailurePromise = Promise.race([ + (async () => { + await oncePromise; + t.fail(); + })(), + new Promise(resolve => { + setTimeout(() => { + resolve(false); + }, 100); + }) + ]); + + oncePromise.off(); + emitter.emit('🦄', fixture); + + await testFailurePromise; + t.pass(); +}); + test.cb('emit() - one event', t => { t.plan(1);