Skip to content

Commit bad60d5

Browse files
committed
refactor: make timeout a thenable
1 parent aa986f8 commit bad60d5

File tree

1 file changed

+18
-20
lines changed

1 file changed

+18
-20
lines changed

src/timeout.ts

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { clearTimeout, setTimeout } from 'timers';
33
import { type Document } from './bson';
44
import { MongoInvalidArgumentError, MongoOperationTimeoutError, MongoRuntimeError } from './error';
55
import { type ClientSession } from './sessions';
6-
import { csotMin, noop } from './utils';
6+
import { csotMin, promiseWithResolvers } from './utils';
77

88
/** @internal */
99
export class TimeoutError extends Error {
@@ -12,9 +12,9 @@ export class TimeoutError extends Error {
1212
return 'TimeoutError';
1313
}
1414

15-
constructor(message: string, options: { cause?: Error; duration: number }) {
15+
constructor(message: string, options?: { cause?: Error; duration?: number }) {
1616
super(message, options);
17-
this.duration = options.duration;
17+
this.duration = options?.duration ?? 0;
1818
}
1919

2020
static is(error: unknown): error is TimeoutError {
@@ -24,22 +24,21 @@ export class TimeoutError extends Error {
2424
}
2525
}
2626

27-
type Executor = ConstructorParameters<typeof Promise<never>>[0];
28-
type Reject = Parameters<ConstructorParameters<typeof Promise<never>>[0]>[1];
2927
/**
3028
* @internal
3129
* This class is an abstraction over timeouts
3230
* The Timeout class can only be in the pending or rejected states. It is guaranteed not to resolve
3331
* if interacted with exclusively through its public API
3432
* */
35-
export class Timeout extends Promise<never> {
33+
export class Timeout implements PromiseLike<never> {
3634
private id?: NodeJS.Timeout;
3735

3836
public readonly start: number;
3937
public ended: number | null = null;
4038
public duration: number;
4139
private timedOut = false;
4240
public cleared = false;
41+
private promise: Promise<never>;
4342

4443
get remainingTime(): number {
4544
if (this.timedOut) return 0;
@@ -52,10 +51,7 @@ export class Timeout extends Promise<never> {
5251
}
5352

5453
/** Create a new timeout that expires in `duration` ms */
55-
private constructor(
56-
executor: Executor = () => null,
57-
options?: { duration: number; unref?: true; rejection?: Error }
58-
) {
54+
private constructor(options?: { duration: number; unref?: true; rejection?: Error }) {
5955
const duration = options?.duration ?? 0;
6056
const unref = !!options?.unref;
6157
const rejection = options?.rejection;
@@ -64,13 +60,8 @@ export class Timeout extends Promise<never> {
6460
throw new MongoInvalidArgumentError('Cannot create a Timeout with a negative duration');
6561
}
6662

67-
let reject!: Reject;
68-
super((_, promiseReject) => {
69-
reject = promiseReject;
70-
71-
executor(noop, promiseReject);
72-
});
73-
63+
const { promise, reject } = promiseWithResolvers<never>();
64+
this.promise = promise;
7465
this.duration = duration;
7566
this.start = Math.trunc(performance.now());
7667

@@ -91,6 +82,13 @@ export class Timeout extends Promise<never> {
9182
}
9283
}
9384

85+
then<_TResult1 = never, TResult2 = never>(
86+
_?: undefined,
87+
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
88+
): PromiseLike<never> {
89+
return this.promise.then<never, never>(undefined, onrejected as any);
90+
}
91+
9492
/**
9593
* Clears the underlying timeout. This method is idempotent
9694
*/
@@ -106,11 +104,11 @@ export class Timeout extends Promise<never> {
106104
}
107105

108106
public static expires(duration: number, unref?: true): Timeout {
109-
return new Timeout(undefined, { duration, unref });
107+
return new Timeout({ duration, unref });
110108
}
111109

112-
static override reject(rejection?: Error): Timeout {
113-
return new Timeout(undefined, { duration: 0, unref: true, rejection });
110+
static reject(rejection?: Error): Timeout {
111+
return new Timeout({ duration: 0, unref: true, rejection });
114112
}
115113
}
116114

0 commit comments

Comments
 (0)