Skip to content

Commit 347c2cc

Browse files
authored
Add support for filter predicate in once() (#124)
1 parent 0b71888 commit 347c2cc

File tree

4 files changed

+87
-8
lines changed

4 files changed

+87
-8
lines changed

index.d.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -462,10 +462,12 @@ export default class Emittery<
462462
): void;
463463

464464
/**
465-
Subscribe to one or more events only once. It will be unsubscribed after the first
466-
event.
465+
Subscribe to one or more events only once. It will be unsubscribed after the first event that matches the predicate (if provided).
467466
468-
@returns The promise of event data when `eventName` is emitted. This promise is extended with an `off` method.
467+
@param eventName - The event name(s) to subscribe to.
468+
@param predicate - Optional predicate function to filter event data. The event will only be emitted if the predicate returns true.
469+
470+
@returns The promise of event data when `eventName` is emitted and predicate matches (if provided). This promise is extended with an `off` method.
469471
470472
@example
471473
```
@@ -482,11 +484,19 @@ export default class Emittery<
482484
console.log(data);
483485
});
484486
487+
// With predicate
488+
emitter.once('data', data => data.ok === true).then(data => {
489+
console.log(data);
490+
//=> {ok: true, value: 42}
491+
});
492+
485493
emitter.emit('🦄', '🌈'); // Logs `🌈` twice
486494
emitter.emit('🐶', '🍖'); // Nothing happens
495+
emitter.emit('data', {ok: false}); // Nothing happens
496+
emitter.emit('data', {ok: true, value: 42}); // Logs {ok: true, value: 42}
487497
```
488498
*/
489-
once<Name extends keyof AllEventData>(eventName: Name | readonly Name[]): EmitteryOncePromise<AllEventData[Name]>;
499+
once<Name extends keyof AllEventData>(eventName: Name | readonly Name[], predicate?: (eventData: AllEventData[Name]) => boolean): EmitteryOncePromise<AllEventData[Name]>;
490500

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

index.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,11 +334,19 @@ export default class Emittery {
334334
}
335335
}
336336

337-
once(eventNames) {
337+
once(eventNames, predicate) {
338+
if (predicate !== undefined && typeof predicate !== 'function') {
339+
throw new TypeError('predicate must be a function');
340+
}
341+
338342
let off_;
339343

340344
const promise = new Promise(resolve => {
341345
off_ = this.on(eventNames, data => {
346+
if (predicate && !predicate(data)) {
347+
return;
348+
}
349+
342350
off_();
343351
resolve(data);
344352
});

readme.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -297,11 +297,11 @@ await emitter.emit('🦊', 'c'); // Nothing happens
297297

298298
##### listener(data)
299299

300-
#### once(eventName | eventName[])
300+
#### once(eventName | eventName[], predicate?)
301301

302-
Subscribe to one or more events only once. It will be unsubscribed after the first event.
302+
Subscribe to one or more events only once. It will be unsubscribed after the first event that matches the predicate (if provided).
303303

304-
Returns a promise for the event data when `eventName` is emitted. This promise is extended with an `off` method.
304+
Returns a promise for the event data when `eventName` is emitted and predicate matches (if provided). This promise is extended with an `off` method.
305305

306306
```js
307307
import Emittery from 'emittery';
@@ -317,8 +317,16 @@ emitter.once(['🦄', '🐶']).then(data => {
317317
console.log(data);
318318
});
319319

320+
// With predicate
321+
emitter.once('data', data => data.ok === true).then(data => {
322+
console.log(data);
323+
//=> {ok: true, value: 42}
324+
});
325+
320326
emitter.emit('🦄', '🌈'); // Log => '🌈' x2
321327
emitter.emit('🐶', '🍖'); // Nothing happens
328+
emitter.emit('data', {ok: false}); // Nothing happens
329+
emitter.emit('data', {ok: true, value: 42}); // Log => {ok: true, value: 42}
322330
```
323331

324332
#### events(eventName)

test/index.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,59 @@ test('once() - returns a promise with an unsubscribe method', async t => {
432432
t.pass();
433433
});
434434

435+
test('once() - supports filter predicate', async t => {
436+
const emitter = new Emittery();
437+
438+
const oncePromise = emitter.once('data', data => data.ok === true);
439+
await emitter.emit('data', {ok: false, foo: 'bar'});
440+
441+
const payload = {ok: true, value: 42};
442+
443+
await emitter.emit('data', payload);
444+
await emitter.emit('data', {ok: true, other: 'value'});
445+
446+
t.is(await oncePromise, payload);
447+
});
448+
449+
test('once() - filter predicate must be a function', t => {
450+
const emitter = new Emittery();
451+
t.throws(
452+
() => emitter.once('data', 'not a function'),
453+
{
454+
instanceOf: TypeError,
455+
message: 'predicate must be a function',
456+
},
457+
);
458+
});
459+
460+
test('once() - filter predicate with multiple event names', async t => {
461+
const emitter = new Emittery();
462+
const payload = {ok: true, value: 42};
463+
464+
const oncePromise = emitter.once(['data1', 'data2'], data => data.ok === true);
465+
await emitter.emit('data1', {ok: false});
466+
await emitter.emit('data2', payload);
467+
468+
t.is(await oncePromise, payload);
469+
});
470+
471+
test('once() - filter predicate can be unsubscribed', async t => {
472+
const emitter = new Emittery();
473+
const oncePromise = emitter.once('data', data => data.ok === true);
474+
475+
oncePromise.off();
476+
await emitter.emit('data', {ok: true});
477+
478+
const testPromise = Promise.race([
479+
oncePromise,
480+
new Promise(resolve => {
481+
setTimeout(() => resolve('timeout'), 100);
482+
}),
483+
]);
484+
485+
t.is(await testPromise, 'timeout');
486+
});
487+
435488
test('emit() - one event', async t => {
436489
const emitter = new Emittery();
437490
const eventFixture = {foo: true};

0 commit comments

Comments
 (0)