Repository: sindresorhus/emittery (js)

Issue:
Support multiple events in `on()`
<!-- Issuehunt Badges -->
[<img alt="Issuehunt badges" src="https://img.shields.io/badge/IssueHunt-%2440%20Funded-%2300A156.svg" />](https://issuehunt.io/r/sindresorhus/emittery/issues/1)
<!-- /Issuehunt Badges -->


I considered adding support for multiple events in `on()`, but wanted to wait to see if this is something people actually need.

Relevant Node.js issue: https://github.com/nodejs/node/issues/15697

// @silverwind


<!-- Issuehunt content -->

---

<details>
<summary>
<b>IssueHunt Summary</b>
</summary>


### Backers (Total: $40.00)

- [<img src='https://avatars3.githubusercontent.com/u/44827199?v=4' alt='issuehunt' width=24 height=24> issuehunt](https://issuehunt.io/u/issuehunt) ($40.00)

### Submitted pull Requests
- [#36 Support multiple events in on, off and once](https://issuehunt.io/r/sindresorhus/emittery/pull/36)
- [#52 support multiple events in on, off and once](https://issuehunt.io/r/sindresorhus/emittery/pull/52)
---

#### [Become a backer now!](https://issuehunt.io/r/sindresorhus/emittery/issues/1)
#### [Or submit a pull request to get the deposits!](https://issuehunt.io/r/sindresorhus/emittery/issues/1)
### Tips

- Checkout the [Issuehunt explorer](https://issuehunt.io/r/sindresorhus/emittery/) to discover more funded issues.
- Need some help from other developers? [Add your repositories](https://issuehunt.io/r/new) on IssueHunt to raise funds.
---
IssueHunt has been backed by the following sponsors. [Become a sponsor](https://issuehunt.io/membership/members)
</details>
<!-- /Issuehunt content-->

Pull request description:
Support listening to multiple events at the same time
Fix #1
This pull request is based on the one by @Angelmmiguel #36

Closes #36


<!-- Issuehunt content -->

---

<details>
<summary>
<b>IssueHunt Summary</b>
</summary>

### Referenced issues

This pull request has been submitted to:
- [#1: Support multiple events in `on()`](https://issuehunt.io/repos/112248999/issues/1)
---

IssueHunt has been backed by the following sponsors. [Become a sponsor](https://issuehunt.io/membership/members)
</details>
<!-- /Issuehunt content-->

Patch:
diff --git a/index.d.ts b/index.d.ts
index d386ddb..690f3c7 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -5,6 +5,11 @@ Symbol event names can be used to avoid name collisions when your classes are ex
 */
 type EventName = string | symbol;
 
+/**
+Emittery also accepts an array of strings and symbols as event names.
+*/
+type EventNames = EventName | readonly EventName[];
+
 declare class Emittery {
 	/**
 	In TypeScript, it returns a decorator which mixins `Emittery` as property `emitteryPropertyName` and `methodNames`, or all `Emittery` methods if `methodNames` is not defined, into the target class.
@@ -78,14 +83,31 @@ declare class Emittery {
 	static readonly listenerRemoved: unique symbol;
 
 	/**
-	Subscribe to an event.
+	Subscribe to one or more events.
 
 	Using the same listener multiple times for the same event will result in only one method call per emitted event.
 
 	@returns An unsubscribe method.
+
+	@example
+	```
+	import Emittery = require('emittery');
+
+	const emitter = new Emittery();
+
+	emitter.on('🦄', data => {
+		console.log(data);
+	});
+	emitter.on(['🦄', '🐶'], data => {
+		console.log(data);
+	});
+
+	emitter.emit('🦄', '🌈'); // log => '🌈' x2
+	emitter.emit('🐶', '🍖'); // log => '🍖'
+	```
 	*/
 	on(eventName: typeof Emittery.listenerAdded | typeof Emittery.listenerRemoved, listener: (eventData: Emittery.ListenerChangedData) => void): Emittery.UnsubscribeFn
-	on(eventName: EventName, listener: (eventData?: unknown) => void): Emittery.UnsubscribeFn;
+	on(eventName: EventNames, listener: (eventData?: unknown) => void): Emittery.UnsubscribeFn;
 
 	/**
 	Get an async iterator which buffers data each time an event is emitted.
@@ -139,22 +161,90 @@ declare class Emittery {
 		}
 	}
 	```
+
+	It accepts multiple event names.
+
+	@example
+	```
+	import Emittery = require('emittery');
+
+	const emitter = new Emittery();
+	const iterator = emitter.events(['🦄', '🦊']);
+
+	emitter.emit('🦄', '🌈1'); // Buffered
+	emitter.emit('🦊', '🌈2'); // Buffered
+
+	iterator
+		.next()
+		.then(({value, done}) => {
+			// done === false
+			// value === '🌈1'
+			return iterator.next();
+		})
+		.then(({value, done}) => {
+			// done === false
+			// value === '🌈2'
+			// Revoke subscription
+			return iterator.return();
+		})
+		.then(({done}) => {
+			// done === true
+		});
+	```
 	*/
-	events(eventName: EventName): AsyncIterableIterator<unknown>
+	events(eventName: EventNames): AsyncIterableIterator<unknown>
 
 	/**
-	Remove an event subscription.
+	Remove one or more event subscriptions.
+
+	@example
+	```
+	import Emittery = require('emittery');
+
+	const emitter = new Emittery();
+
+	const listener = data => console.log(data);
+	(async () => {
+		emitter.on(['🦄', '🐶', '🦊'], listener);
+		await emitter.emit('🦄', 'a');
+		await emitter.emit('🐶', 'b');
+		await emitter.emit('🦊', 'c');
+		emitter.off('🦄', listener);
+		emitter.off(['🐶', '🦊'], listener);
+		await emitter.emit('🦄', 'a'); // nothing happens
+		await emitter.emit('🐶', 'b'); // nothing happens
+		await emitter.emit('🦊', 'c'); // nothing happens
+	})();
+	```
 	*/
-	off(eventName: EventName, listener: (eventData?: unknown) => void): void;
+	off(eventName: EventNames, listener: (eventData?: unknown) => void): void;
 
 	/**
-	Subscribe to an event only once. It will be unsubscribed after the first
+	Subscribe to one or more events only once. It will be unsubscribed after the first
 	event.
 
 	@returns The event data when `eventName` is emitted.
+
+	@example
+	```
+	import Emittery = require('emittery');
+
+	const emitter = new Emittery();
+
+	emitter.once('🦄').then(data => {
+		console.log(data);
+		//=> '🌈'
+	});
+	emitter.once(['🦄', '🐶']).then(data => {
+		console.log(data);
+	});
+
+	emitter.emit('🦄', '🌈'); // Logs `🌈` twice
+	emitter.emit('🐶', '🍖'); // Nothing happens
+	```
 	*/
 	once(eventName: typeof Emittery.listenerAdded | typeof Emittery.listenerRemoved): Promise<Emittery.ListenerChangedData>
-	once(eventName: EventName): Promise<unknown>;
+	once(eventName: EventNames): Promise<unknown>;
 
 	/**
 	Trigger an event asynchronously, optionally with some data. Listeners are called in the order they were added, but executed concurrently.
@@ -225,12 +315,12 @@ declare class Emittery {
 
 	If `eventName` is given, only the listeners for that event are cleared.
 	*/
-	clearListeners(eventName?: EventName): void;
+	clearListeners(eventName?: EventNames): void;
 
 	/**
 	The number of listeners for the `eventName` or all events if not specified.
 	*/
-	listenerCount(eventName?: EventName): number;
+	listenerCount(eventName?: EventNames): number;
 
 	/**
 	Bind the given `methodNames`, or all `Emittery` methods if `methodNames` is not defined, into the `target` object.
diff --git a/index.js b/index.js
index 5ddd3db..1b528ed 100644
--- a/index.js
+++ b/index.js
@@ -56,7 +56,9 @@ function enqueueProducers(instance, eventName, eventData) {
 	}
 }
 
-function iterator(instance, eventName) {
+function iterator(instance, eventNames) {
+	eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
+
 	let isFinished = false;
 	let flush = () => {};
 	let queue = [];
@@ -72,7 +74,9 @@ function iterator(instance, eventName) {
 		}
 	};
 
-	getEventProducers(instance, eventName).add(producer);
+	for (const eventName of eventNames) {
+		getEventProducers(instance, eventName).add(producer);
+	}
 
 	return {
 		async next() {
@@ -101,7 +105,11 @@ function iterator(instance, eventName) {
 
 		async return(value) {
 			queue = undefined;
-			getEventProducers(instance, eventName).delete(producer);
+
+			for (const eventName of eventNames) {
+				getEventProducers(instance, eventName).delete(producer);
+			}
+
 			flush();
 
 			return arguments.length > 0 ?
@@ -187,42 +195,52 @@ class Emittery {
 		producersMap.set(this, new Map());
 	}
 
-	on(eventName, listener) {
-		assertEventName(eventName);
+	on(eventNames, listener) {
 		assertListener(listener);
-		getListeners(this, eventName).add(listener);
 
-		if (!isListenerSymbol(eventName)) {
-			this.emit(listenerAdded, {eventName, listener});
+		eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
+		for (const eventName of eventNames) {
+			assertEventName(eventName);
+			getListeners(this, eventName).add(listener);
+
+			if (!isListenerSymbol(eventName)) {
+				this.emit(listenerAdded, {eventName, listener});
+			}
 		}
 
-		return this.off.bind(this, eventName, listener);
+		return this.off.bind(this, eventNames, listener);
 	}
 
-	off(eventName, listener) {
-		assertEventName(eventName);
+	off(eventNames, listener) {
 		assertListener(listener);
 
-		if (!isListenerSymbol(eventName)) {
-			this.emit(listenerRemoved, {eventName, listener});
-		}
+		eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
+		for (const eventName of eventNames) {
+			assertEventName(eventName);
+			getListeners(this, eventName).delete(listener);
 
-		getListeners(this, eventName).delete(listener);
+			if (!isListenerSymbol(eventName)) {
+				this.emit(listenerRemoved, {eventName, listener});
+			}
+		}
 	}
 
-	once(eventName) {
+	once(eventNames) {
 		return new Promise(resolve => {
-			assertEventName(eventName);
-			const off = this.on(eventName, data => {
+			const off = this.on(eventNames, data => {
 				off();
 				resolve(data);
 			});
 		});
 	}
 
-	events(eventName) {
-		assertEventName(eventName);
-		return iterator(this, eventName);
+	events(eventNames) {
+		eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
+		for (const eventName of eventNames) {
+			assertEventName(eventName);
+		}
+
+		return iterator(this, eventNames);
 	}
 
 	async emit(eventName, eventData) {
@@ -236,7 +254,7 @@ class Emittery {
 		const staticAnyListeners = isListenerSymbol(eventName) ? [] : [...anyListeners];
 
 		await resolvedPromise;
-		return Promise.all([
+		await Promise.all([
 			...staticListeners.map(async listener => {
 				if (listeners.has(listener)) {
 					return listener(eventData);
@@ -291,52 +309,62 @@ class Emittery {
 		anyMap.get(this).delete(listener);
 	}
 
-	clearListeners(eventName) {
-		if (typeof eventName === 'string') {
-			getListeners(this, eventName).clear();
+	clearListeners(eventNames) {
+		eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
 
-			const producers = getEventProducers(this, eventName);
+		for (const eventName of eventNames) {
+			if (typeof eventName === 'string') {
+				getListeners(this, eventName).clear();
 
-			for (const producer of producers) {
-				producer.finish();
-			}
-
-			producers.clear();
-		} else {
-			anyMap.get(this).clear();
+				const producers = getEventProducers(this, eventName);
 
-			for (const listeners of eventsMap.get(this).values()) {
-				listeners.clear();
-			}
-
-			for (const producers of producersMap.get(this).values()) {
 				for (const producer of producers) {
 					producer.finish();
 				}
 
 				producers.clear();
+			} else {
+				anyMap.get(this).clear();
+
+				for (const listeners of eventsMap.get(this).values()) {
+					listeners.clear();
+				}
+
+				for (const producers of producersMap.get(this).values()) {
+					for (const producer of producers) {
+						producer.finish();
+					}
+
+					producers.clear();
+				}
 			}
 		}
 	}
 
-	listenerCount(eventName) {
-		if (typeof eventName === 'string') {
-			return anyMap.get(this).size + getListeners(this, eventName).size +
-				getEventProducers(this, eventName).size + getEventProducers(this).size;
-		}
+	listenerCount(eventNames) {
+		eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
+		let count = 0;
 
-		if (typeof eventName !== 'undefined') {
-			assertEventName(eventName);
-		}
+		for (const eventName of eventNames) {
+			if (typeof eventName === 'string') {
+				count += anyMap.get(this).size + getListeners(this, eventName).size +
+					getEventProducers(this, eventName).size + getEventProducers(this).size;
+				continue;
+			}
 
-		let count = anyMap.get(this).size;
+			if (typeof eventName !== 'undefined') {
+				assertEventName(eventName);
+			}
 
-		for (const value of eventsMap.get(this).values()) {
-			count += value.size;
-		}
+			count += anyMap.get(this).size;
+
+			for (const value of eventsMap.get(this).values()) {
+				count += value.size;
+			}
 
-		for (const value of producersMap.get(this).values()) {
-			count += value.size;
+			for (const value of producersMap.get(this).values()) {
+				count += value.size;
+			}
 		}
 
 		return count;
diff --git a/readme.md b/readme.md
index 2cbbedd..c873549 100644
--- a/readme.md
+++ b/readme.md
@@ -45,14 +45,30 @@ Symbol event names can be used to avoid name collisions when your classes are ex
 
 ### emitter = new Emittery()
 
-#### on(eventName, listener)
+#### on(eventName | eventName[], listener)
 
-Subscribe to an event.
+Subscribe to one or more events.
 
 Returns an unsubscribe method.
 
 Using the same listener multiple times for the same event will result in only one method call per emitted event.
 
+```js
+const Emittery = require('emittery');
+
+const emitter = new Emittery();
+
+emitter.on('🦄', data => {
+	console.log(data);
+});
+emitter.on(['🦄', '🐶'], data => {
+	console.log(data);
+});
+
+emitter.emit('🦄', '🌈'); // log => '🌈' x2
+emitter.emit('🐶', '🍖'); // log => '🍖'
+```
+
 ##### Custom subscribable events
 
 Emittery exports some symbols which represent custom events that can be passed to `Emitter.on` and similar methods.
@@ -87,15 +103,34 @@ Only events that are not of this type are able to trigger these events.
 
 ##### listener(data)
 
-#### off(eventName, listener)
+#### off(eventName | eventName[], listener)
+
+Remove one or more event subscriptions.
+
+```js
+const Emittery = require('emittery');
+
+const emitter = new Emittery();
 
-Remove an event subscription.
+const listener = data => console.log(data);
+(async () => {
+	emitter.on(['🦄', '🐶', '🦊'], listener);
+	await emitter.emit('🦄', 'a');
+	await emitter.emit('🐶', 'b');
+	await emitter.emit('🦊', 'c');
+	emitter.off('🦄', listener);
+	emitter.off(['🐶', '🦊'], listener);
+	await emitter.emit('🦄', 'a'); // nothing happens
+	await emitter.emit('🐶', 'b'); // nothing happens
+	await emitter.emit('🦊', 'c'); // nothing happens
+})();
+```
 
 ##### listener(data)
 
-#### once(eventName)
+#### once(eventName | eventName[])
 
-Subscribe to an event only once. It will be unsubscribed after the first event.
+Subscribe to one or more events only once. It will be unsubscribed after the first event.
 
 Returns a promise for the event data when `eventName` is emitted.
 
@@ -108,8 +143,12 @@ emitter.once('🦄').then(data => {
 	console.log(data);
 	//=> '🌈'
 });
+emitter.once(['🦄', '🐶']).then(data => {
+	console.log(data);
+});
 
-emitter.emit('🦄', '🌈');
+emitter.emit('🦄', '🌈'); // log => '🌈' x2
+emitter.emit('🐶', '🍖'); // nothing happens
 ```
 
 #### events(eventName)
@@ -164,6 +203,35 @@ for await (const data of iterator) {
 }
 ```
 
+It accepts multiple event names.
+
+```js
+const Emittery = require('emittery');
+
+const emitter = new Emittery();
+const iterator = emitter.events(['🦄', '🦊']);
+
+emitter.emit('🦄', '🌈1'); // Buffered
+emitter.emit('🦊', '🌈2'); // Buffered
+
+iterator
+	.next()
+	.then(({value, done}) => {
+		// done === false
+		// value === '🌈1'
+		return iterator.next();
+	})
+	.then(({value, done}) => {
+		// done === false
+		// value === '🌈2'
+		// Revoke subscription
+		return iterator.return();
+	})
+	.then(({done}) => {
+		// done === true
+	});
+```
+
 #### emit(eventName, data?)
 
 Trigger an event asynchronously, optionally with some data. Listeners are called in the order they were added, but executed concurrently.
@@ -222,15 +290,15 @@ iterator.next()
 
 In the same way as for `events`, you can subscribe by using the `for await` statement
 
-#### clearListeners()
+#### clearListeners(eventNames?)
 
 Clear all event listeners on the instance.
 
-If `eventName` is given, only the listeners for that event are cleared.
+If `eventNames` is given, only the listeners for that events are cleared.
 
-#### listenerCount(eventName?)
+#### listenerCount(eventNames?)
 
-The number of listeners for the `eventName` or all events if not specified.
+The number of listeners for the `eventNames` or all events if not specified.
 
 #### bindMethods(target, methodNames?)
 


Test patch:
diff --git a/test/index.js b/test/index.js
index 6b1d838..42f0fb6 100644
--- a/test/index.js
+++ b/test/index.js
@@ -5,21 +5,55 @@ import Emittery from '..';
 
 test('on()', async t => {
 	const emitter = new Emittery();
+	const eventName = Symbol('eventName');
 	const calls = [];
-	const listener1 = () => calls.push(1);
-	const listener2 = () => calls.push(2);
+	const listener1 = () => {
+		calls.push(1);
+	};
+
+	const listener2 = () => {
+		calls.push(2);
+	};
+
+	const listener3 = () => {
+		calls.push(3);
+	};
+
 	emitter.on('🦄', listener1);
 	emitter.on('🦄', listener2);
+	emitter.on(eventName, listener3);
 	await emitter.emit('🦄');
-	t.deepEqual(calls, [1, 2]);
+	await emitter.emit(eventName);
+	t.deepEqual(calls, [1, 2, 3]);
+});
+
+test('on() - multiple event names', async t => {
+	const emitter = new Emittery();
+	const eventName = Symbol('eventName');
+	let count = 0;
+	const listener = () => {
+		++count;
+	};
+
+	emitter.on(['🦄', '🐶', eventName], listener);
+	await emitter.emit('🦄');
+	await emitter.emit('🐶');
+	await emitter.emit(eventName);
+	t.is(count, 3);
 });
 
 test('on() - symbol eventName', async t => {
 	const emitter = new Emittery();
 	const eventName = Symbol('eventName');
 	const calls = [];
-	const listener1 = () => calls.push(1);
-	const listener2 = () => calls.push(2);
+	const listener1 = () => {
+		calls.push(1);
+	};
+
+	const listener2 = () => {
+		calls.push(2);
+	};
+
 	emitter.on(eventName, listener1);
 	emitter.on(eventName, listener2);
 	await emitter.emit(eventName);
@@ -100,7 +134,9 @@ test('on() - must have a listener', t => {
 test('on() - returns a unsubcribe method', async t => {
 	const emitter = new Emittery();
 	const calls = [];
-	const listener = () => calls.push(1);
+	const listener = () => {
+		calls.push(1);
+	};
 
 	const off = emitter.on('🦄', listener);
 	await emitter.emit('🦄');
@@ -114,7 +150,9 @@ test('on() - returns a unsubcribe method', async t => {
 test('on() - dedupes identical listeners', async t => {
 	const emitter = new Emittery();
 	const calls = [];
-	const listener = () => calls.push(1);
+	const listener = () => {
+		calls.push(1);
+	};
 
 	emitter.on('🦄', listener);
 	emitter.on('🦄', listener);
@@ -144,6 +182,28 @@ test.serial('events()', async t => {
 	t.deepEqual(await iterator.next(), {done: true});
 });
 
+test.serial('events() - multiple event names', async t => {
+	const emitter = new Emittery();
+	const iterator = emitter.events(['🦄', '🐶']);
+
+	await emitter.emit('🦄', '🌈');
+	await emitter.emit('🐶', '🌈');
+	setTimeout(() => {
+		emitter.emit('🦄', Promise.resolve('🌟'));
+	}, 10);
+
+	t.plan(4);
+	const expected = ['🌈', '🌈', '🌟'];
+	for await (const data of iterator) {
+		t.deepEqual(data, expected.shift());
+		if (expected.length === 0) {
+			break;
+		}
+	}
+
+	t.deepEqual(await iterator.next(), {done: true});
+});
+
 test('events() - return() called during emit', async t => {
 	const emitter = new Emittery();
 	let iterator = null;
@@ -190,7 +250,9 @@ test('events() - discarded iterators should stop receiving events', async t => {
 test('off()', async t => {
 	const emitter = new Emittery();
 	const calls = [];
-	const listener = () => calls.push(1);
+	const listener = () => {
+		calls.push(1);
+	};
 
 	emitter.on('🦄', listener);
 	await emitter.emit('🦄');
@@ -201,6 +263,26 @@ test('off()', async t => {
 	t.deepEqual(calls, [1]);
 });
 
+test('off() - multiple event names', async t => {
+	const emitter = new Emittery();
+	const calls = [];
+	const listener = () => {
+		calls.push(1);
+	};
+
+	emitter.on(['🦄', '🐶', '🦊'], listener);
+	await emitter.emit('🦄');
+	t.deepEqual(calls, [1]);
+
+	emitter.off(['🦄', '🐶'], listener);
+	await emitter.emit('🦄');
+	await emitter.emit('🐶');
+	t.deepEqual(calls, [1]);
+
+	await emitter.emit('🦊');
+	t.deepEqual(calls, [1, 1]);
+});
+
 test('off() - eventName must be a string or a symbol', t => {
 	const emitter = new Emittery();
 
@@ -228,6 +310,14 @@ test('once()', async t => {
 	t.is(await promise, fixture);
 });
 
+test('once() - multiple event names', async t => {
+	const fixture = '🌈';
+	const emitter = new Emittery();
+	const promise = emitter.once(['🦄', '🐶']);
+	emitter.emit('🐶', fixture);
+	t.is(await promise, fixture);
+});
+
 test('once() - eventName must be a string or a symbol', async t => {
 	const emitter = new Emittery();
 
@@ -302,16 +392,22 @@ test.cb('emit() - is async', t => {
 test('emit() - calls listeners subscribed when emit() was invoked', async t => {
 	const emitter = new Emittery();
 	const calls = [];
-	const off1 = emitter.on('🦄', () => calls.push(1));
+	const off1 = emitter.on('🦄', () => {
+		calls.push(1);
+	});
 	const p = emitter.emit('🦄');
-	emitter.on('🦄', () => calls.push(2));
+	emitter.on('🦄', () => {
+		calls.push(2);
+	});
 	await p;
 	t.deepEqual(calls, [1]);
 
 	const off3 = emitter.on('🦄', () => {
 		calls.push(3);
 		off1();
-		emitter.on('🦄', () => calls.push(4));
+		emitter.on('🦄', () => {
+			calls.push(4);
+		});
 	});
 	await emitter.emit('🦄');
 	t.deepEqual(calls, [1, 1, 2, 3]);
@@ -319,7 +415,9 @@ test('emit() - calls listeners subscribed when emit() was invoked', async t => {
 
 	const off5 = emitter.on('🦄', () => {
 		calls.push(5);
-		emitter.onAny(() => calls.push(6));
+		emitter.onAny(() => {
+			calls.push(6);
+		});
 	});
 	await emitter.emit('🦄');
 	t.deepEqual(calls, [1, 1, 2, 3, 2, 4, 5]);
@@ -330,7 +428,9 @@ test('emit() - calls listeners subscribed when emit() was invoked', async t => {
 		calls.push(7);
 		off8();
 	});
-	off8 = emitter.on('🦄', () => calls.push(8));
+	off8 = emitter.on('🦄', () => {
+		calls.push(8);
+	});
 	await emitter.emit('🦄');
 	t.deepEqual(calls, [1, 1, 2, 3, 2, 4, 5, 2, 4, 7, 6]);
 
@@ -339,7 +439,9 @@ test('emit() - calls listeners subscribed when emit() was invoked', async t => {
 		calls.push(9);
 		off10();
 	});
-	off10 = emitter.onAny(() => calls.push(10));
+	off10 = emitter.onAny(() => {
+		calls.push(10);
+	});
 	await emitter.emit('🦄');
 	t.deepEqual(calls, [1, 1, 2, 3, 2, 4, 5, 2, 4, 7, 6, 2, 4, 7, 6, 9]);
 
@@ -352,6 +454,30 @@ test('emit() - calls listeners subscribed when emit() was invoked', async t => {
 	t.deepEqual(calls, [1, 1, 2, 3, 2, 4, 5, 2, 4, 7, 6, 2, 4, 7, 6, 9, 2, 4, 7, 6, 9]);
 });
 
+test('emit() - returns undefined', async t => {
+	const emitter = new Emittery();
+
+	emitter.on('🦄', () => '🌈');
+	t.is(await emitter.emit('🦄'), undefined);
+
+	emitter.on('🦄🦄', async () => '🌈');
+	t.is(await emitter.emit('🦄🦄'), undefined);
+});
+
+test('emit() - throws an error if any listener throws', async t => {
+	const emitter = new Emittery();
+
+	emitter.on('🦄', () => {
+		throw new Error('🌈');
+	});
+	await t.throwsAsync(emitter.emit('🦄'), {instanceOf: Error});
+
+	emitter.on('🦄🦄', async () => {
+		throw new Error('🌈');
+	});
+	await t.throwsAsync(emitter.emit('🦄🦄'), {instanceOf: Error});
+});
+
 test.cb('emitSerial()', t => {
 	t.plan(1);
 
@@ -406,16 +532,22 @@ test.cb('emitSerial() - is async', t => {
 test('emitSerial() - calls listeners subscribed when emitSerial() was invoked', async t => {
 	const emitter = new Emittery();
 	const calls = [];
-	const off1 = emitter.on('🦄', () => calls.push(1));
+	const off1 = emitter.on('🦄', () => {
+		calls.push(1);
+	});
 	const p = emitter.emitSerial('🦄');
-	emitter.on('🦄', () => calls.push(2));
+	emitter.on('🦄', () => {
+		calls.push(2);
+	});
 	await p;
 	t.deepEqual(calls, [1]);
 
 	const off3 = emitter.on('🦄', () => {
 		calls.push(3);
 		off1();
-		emitter.on('🦄', () => calls.push(4));
+		emitter.on('🦄', () => {
+			calls.push(4);
+		});
 	});
 	await emitter.emitSerial('🦄');
 	t.deepEqual(calls, [1, 1, 2, 3]);
@@ -423,7 +555,9 @@ test('emitSerial() - calls listeners subscribed when emitSerial() was invoked',
 
 	const off5 = emitter.on('🦄', () => {
 		calls.push(5);
-		emitter.onAny(() => calls.push(6));
+		emitter.onAny(() => {
+			calls.push(6);
+		});
 	});
 	await emitter.emitSerial('🦄');
 	t.deepEqual(calls, [1, 1, 2, 3, 2, 4, 5]);
@@ -434,7 +568,9 @@ test('emitSerial() - calls listeners subscribed when emitSerial() was invoked',
 		calls.push(7);
 		off8();
 	});
-	off8 = emitter.on('🦄', () => calls.push(8));
+	off8 = emitter.on('🦄', () => {
+		calls.push(8);
+	});
 	await emitter.emitSerial('🦄');
 	t.deepEqual(calls, [1, 1, 2, 3, 2, 4, 5, 2, 4, 7, 6]);
 
@@ -443,7 +579,9 @@ test('emitSerial() - calls listeners subscribed when emitSerial() was invoked',
 		calls.push(9);
 		off10();
 	});
-	off10 = emitter.onAny(() => calls.push(10));
+	off10 = emitter.onAny(() => {
+		calls.push(10);
+	});
 	await emitter.emitSerial('🦄');
 	t.deepEqual(calls, [1, 1, 2, 3, 2, 4, 5, 2, 4, 7, 6, 2, 4, 7, 6, 9]);
 
@@ -534,7 +672,10 @@ test('anyEvents() - discarded iterators should stop receiving events', async t =
 test('offAny()', async t => {
 	const emitter = new Emittery();
 	const calls = [];
-	const listener = () => calls.push(1);
+	const listener = () => {
+		calls.push(1);
+	};
+
 	emitter.onAny(listener);
 	await emitter.emit('🦄');
 	t.deepEqual(calls, [1]);
@@ -554,11 +695,21 @@ test('offAny() - no listener', t => {
 test('clearListeners()', async t => {
 	const emitter = new Emittery();
 	const calls = [];
-	emitter.on('🦄', () => calls.push('🦄1'));
-	emitter.on('🌈', () => calls.push('🌈'));
-	emitter.on('🦄', () => calls.push('🦄2'));
-	emitter.onAny(() => calls.push('any1'));
-	emitter.onAny(() => calls.push('any2'));
+	emitter.on('🦄', () => {
+		calls.push('🦄1');
+	});
+	emitter.on('🌈', () => {
+		calls.push('🌈');
+	});
+	emitter.on('🦄', () => {
+		calls.push('🦄2');
+	});
+	emitter.onAny(() => {
+		calls.push('any1');
+	});
+	emitter.onAny(() => {
+		calls.push('any2');
+	});
 	await emitter.emit('🦄');
 	await emitter.emit('🌈');
 	t.deepEqual(calls, ['🦄1', '🦄2', 'any1', 'any2', '🌈', 'any1', 'any2']);
@@ -589,11 +740,21 @@ test('clearListeners() - also clears iterators', async t => {
 test('clearListeners() - with event name', async t => {
 	const emitter = new Emittery();
 	const calls = [];
-	emitter.on('🦄', () => calls.push('🦄1'));
-	emitter.on('🌈', () => calls.push('🌈'));
-	emitter.on('🦄', () => calls.push('🦄2'));
-	emitter.onAny(() => calls.push('any1'));
-	emitter.onAny(() => calls.push('any2'));
+	emitter.on('🦄', () => {
+		calls.push('🦄1');
+	});
+	emitter.on('🌈', () => {
+		calls.push('🌈');
+	});
+	emitter.on('🦄', () => {
+		calls.push('🦄2');
+	});
+	emitter.onAny(() => {
+		calls.push('any1');
+	});
+	emitter.onAny(() => {
+		calls.push('any2');
+	});
 	await emitter.emit('🦄');
 	await emitter.emit('🌈');
 	t.deepEqual(calls, ['🦄1', '🦄2', 'any1', 'any2', '🌈', 'any1', 'any2']);
@@ -603,6 +764,30 @@ test('clearListeners() - with event name', async t => {
 	t.deepEqual(calls, ['🦄1', '🦄2', 'any1', 'any2', '🌈', 'any1', 'any2', 'any1', 'any2', '🌈', 'any1', 'any2']);
 });
 
+test('clearListeners() - with multiple event names', async t => {
+	const emitter = new Emittery();
+	const calls = [];
+	emitter.on('🦄', () => {
+		calls.push('🦄1');
+	});
+	emitter.on('🌈', () => {
+		calls.push('🌈');
+	});
+	emitter.on('🦄', () => {
+		calls.push('🦄2');
+	});
+	emitter.onAny(() => {
+		calls.push('any1');
+	});
+	await emitter.emit('🦄');
+	await emitter.emit('🌈');
+	t.deepEqual(calls, ['🦄1', '🦄2', 'any1', '🌈', 'any1']);
+	emitter.clearListeners(['🦄', '🌈']);
+	await emitter.emit('🦄');
+	await emitter.emit('🌈');
+	t.deepEqual(calls, ['🦄1', '🦄2', 'any1', '🌈', 'any1', 'any1', 'any1']);
+});
+
 test('clearListeners() - with event name - clears iterators for that event', async t => {
 	const emitter = new Emittery();
 	const iterator = emitter.events('🦄');
@@ -633,6 +818,17 @@ test('listenerCount()', t => {
 	t.is(emitter.listenerCount(), 5);
 });
 
+test('listenerCount() - multiple event names', t => {
+	const emitter = new Emittery();
+	emitter.on('🦄', () => {});
+	emitter.on('🌈', () => {});
+	emitter.on('🦄', () => {});
+	emitter.onAny(() => {});
+	emitter.onAny(() => {});
+	t.is(emitter.listenerCount(['🦄', '🌈']), 7);
+	t.is(emitter.listenerCount(), 5);
+});
+
 test('listenerCount() - works with empty eventName strings', t => {
 	const emitter = new Emittery();
 	emitter.on('', () => {});