Skip to content

Commit dbd359f

Browse files
committed
refactor: make timeout a thenable
1 parent ed25d56 commit dbd359f

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
@@ -2,7 +2,7 @@ import { clearTimeout, setTimeout } from 'timers';
22

33
import { MongoInvalidArgumentError, MongoOperationTimeoutError, MongoRuntimeError } from './error';
44
import { type ClientSession } from './sessions';
5-
import { csotMin, noop } from './utils';
5+
import { csotMin, promiseWithResolvers } from './utils';
66

77
/** @internal */
88
export class TimeoutError extends Error {
@@ -11,9 +11,9 @@ export class TimeoutError extends Error {
1111
return 'TimeoutError';
1212
}
1313

14-
constructor(message: string, options: { cause?: Error; duration: number }) {
14+
constructor(message: string, options?: { cause?: Error; duration?: number }) {
1515
super(message, options);
16-
this.duration = options.duration;
16+
this.duration = options?.duration ?? 0;
1717
}
1818

1919
static is(error: unknown): error is TimeoutError {
@@ -23,22 +23,21 @@ export class TimeoutError extends Error {
2323
}
2424
}
2525

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

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

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

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

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

@@ -90,6 +81,13 @@ export class Timeout extends Promise<never> {
9081
}
9182
}
9283

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

107105
public static expires(duration: number, unref?: true): Timeout {
108-
return new Timeout(undefined, { duration, unref });
106+
return new Timeout({ duration, unref });
109107
}
110108

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

0 commit comments

Comments
 (0)