Skip to content

Commit 9f83f87

Browse files
committed
Add an off method to promise returned from once
1 parent bf84337 commit 9f83f87

File tree

4 files changed

+44
-5
lines changed

4 files changed

+44
-5
lines changed

index.d.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,13 @@ interface Options<EventData> {
132132
debug?: DebugOptions<EventData>;
133133
}
134134

135+
/**
136+
A promise returned from `emittery.once` with an extra `off` method to cancel your subscription.
137+
*/
138+
interface EmitteryOncePromise<T> extends Promise<T> {
139+
off(): void;
140+
}
141+
135142
/**
136143
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`.
137144
@@ -436,7 +443,7 @@ declare class Emittery<
436443
Subscribe to one or more events only once. It will be unsubscribed after the first
437444
event.
438445
439-
@returns The event data when `eventName` is emitted.
446+
@returns The promise of event data when `eventName` is emitted. This promise is extended with an `off` method.
440447
441448
@example
442449
```
@@ -457,7 +464,7 @@ declare class Emittery<
457464
emitter.emit('🐶', '🍖'); // Nothing happens
458465
```
459466
*/
460-
once<Name extends keyof AllEventData>(eventName: Name | Name[]): Promise<AllEventData[Name]>;
467+
once<Name extends keyof AllEventData>(eventName: Name | Name[]): EmitteryOncePromise<AllEventData[Name]>;
461468

462469
/**
463470
Trigger an event asynchronously, optionally with some data. Listeners are called in the order they were added, but executed concurrently.

index.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -283,12 +283,17 @@ class Emittery {
283283
}
284284

285285
once(eventNames) {
286-
return new Promise(resolve => {
287-
const off = this.on(eventNames, data => {
288-
off();
286+
let off_;
287+
288+
const promise = new Promise(resolve => {
289+
off_ = this.on(eventNames, data => {
290+
off_();
289291
resolve(data);
290292
});
291293
});
294+
295+
promise.off = off_;
296+
return promise;
292297
}
293298

294299
events(eventNames) {

index.test-d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ type AnyListener = (eventData?: unknown) => void | Promise<void>;
5454
expectType<PropertyKey | undefined>(eventName);
5555
expectType<AnyListener>(listener);
5656
});
57+
const oncePromise = ee.once('anotherEvent');
58+
oncePromise.off();
59+
await oncePromise;
5760
};
5861
}
5962

test/index.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,30 @@ test('once() - eventName must be a string, symbol, or number', async t => {
374374
await t.throwsAsync(emitter.once(true), TypeError);
375375
});
376376

377+
test('once() - returns a promise with an unsubscribe method', async t => {
378+
const fixture = '🌈';
379+
const emitter = new Emittery();
380+
const oncePromise = emitter.once('🦄');
381+
382+
const testFailurePromise = Promise.race([
383+
(async () => {
384+
await oncePromise;
385+
t.fail();
386+
})(),
387+
new Promise(resolve => {
388+
setTimeout(() => {
389+
resolve(false);
390+
}, 100);
391+
})
392+
]);
393+
394+
oncePromise.off();
395+
emitter.emit('🦄', fixture);
396+
397+
await testFailurePromise;
398+
t.pass();
399+
});
400+
377401
test.cb('emit() - one event', t => {
378402
t.plan(1);
379403

0 commit comments

Comments
 (0)