Repository: sindresorhus/got
Pull request #2092 (2022-07-25 11:30:34)

Remove event listener feature
Added possibility to remove event listeners.

This solves my feature request from here https://github.com/sindresorhus/got/issues/2087

#### Checklist

- [x] I have read the documentation.
- [x] I have included a pull request description of my changes.
- [x] I have included some tests.
- [x] If it's a new feature, I have included documentation updates in both the README and the types.


Rychu-Pawel: Sorry for the close and reopen stuff...

Patch:
diff --git a/documentation/1-promise.md b/documentation/1-promise.md
index 952b6cd..3f5c536 100644
--- a/documentation/1-promise.md
+++ b/documentation/1-promise.md
@@ -89,3 +89,28 @@ Whether the promise is canceled.
 ### `promise.on(event, handler)`
 
 The events are the same as in [Stream API](3-streams.md#events).
+
+### `promise.off(event, handler)`
+
+Removes listener registered with [`promise.on`](1-promise.md#promiseonevent-handler)
+
+```js
+import {createReadStream} from 'node:fs';
+import got from 'got';
+
+const ongoingRequestPromise = got.post(uploadUrl, {
+    body: createReadStream('sample.txt')
+});
+
+const eventListener = (progress: Progress) => {
+    console.log(progress);
+};
+
+ongoingRequestPromise.on('uploadProgress', eventListener);
+
+setTimeout(() => {
+    ongoingRequestPromise.off('uploadProgress', eventListener);
+}, 500);
+
+await ongoingRequestPromise;
+```
diff --git a/source/as-promise/index.ts b/source/as-promise/index.ts
index 95ce06d..5e7bd2b 100644
--- a/source/as-promise/index.ts
+++ b/source/as-promise/index.ts
@@ -170,6 +170,11 @@ export default function asPromise<T>(firstRequest?: Request): CancelableRequest<
 		return promise;
 	};
 
+	promise.off = (event: string, fn: (...args: any[]) => void) => {
+		emitter.off(event, fn);
+		return promise;
+	};
+
 	const shortcut = <T>(responseType: Options['responseType']): CancelableRequest<T> => {
 		const newPromise = (async () => {
 			// Wait until downloading has ended
diff --git a/source/core/index.ts b/source/core/index.ts
index 01a1212..079d349 100644
--- a/source/core/index.ts
+++ b/source/core/index.ts
@@ -117,6 +117,7 @@ export type GotEventFunction<T> =
 export interface RequestEvents<T> {
 	on: GotEventFunction<T>;
 	once: GotEventFunction<T>;
+	off: GotEventFunction<T>;
 }
 
 export type CacheableRequestFunction = (


Test patch:
diff --git a/test/progress.ts b/test/progress.ts
index ffd5ad1..26924f1 100644
--- a/test/progress.ts
+++ b/test/progress.ts
@@ -221,3 +221,44 @@ test('upload progress - no body', withServer, async (t, server, got) => {
 		},
 	]);
 });
+
+test('upload progress - no events when immediatly removed listener', withServer, async (t, server, got) => {
+	server.post('/', uploadEndpoint);
+
+	const events: Progress[] = [];
+
+	const listener = (event: Progress) => events.push(event);
+
+	const promise = got.post('')
+		.on('uploadProgress', listener)
+		.off('uploadProgress', listener);
+
+	await promise;
+
+	t.is(events.length, 0);
+});
+
+test('upload progress - one event when removed listener', withServer, async (t, server, got) => {
+	server.post('/', uploadEndpoint);
+
+	const events: Progress[] = [];
+
+	const promise = got.post('');
+
+	const listener = (event: Progress) => {
+		events.push(event);
+		void promise.off('uploadProgress', listener);
+	};
+
+	void promise.on('uploadProgress', listener);
+
+	await promise;
+
+	t.deepEqual(events, [
+		{
+			percent: 0,
+			transferred: 0,
+			total: undefined,
+		},
+	]);
+});