diff --git a/src/timeout.ts b/src/timeout.ts index 47c27c7b90..f345fccb1a 100644 --- a/src/timeout.ts +++ b/src/timeout.ts @@ -2,7 +2,7 @@ import { clearTimeout, setTimeout } from 'timers'; import { MongoInvalidArgumentError, MongoOperationTimeoutError, MongoRuntimeError } from './error'; import { type ClientSession } from './sessions'; -import { csotMin, noop } from './utils'; +import { csotMin, promiseWithResolvers } from './utils'; /** @internal */ export class TimeoutError extends Error { @@ -11,9 +11,9 @@ export class TimeoutError extends Error { return 'TimeoutError'; } - constructor(message: string, options: { cause?: Error; duration: number }) { + constructor(message: string, options?: { cause?: Error; duration?: number }) { super(message, options); - this.duration = options.duration; + this.duration = options?.duration ?? 0; } static is(error: unknown): error is TimeoutError { @@ -23,15 +23,13 @@ export class TimeoutError extends Error { } } -type Executor = ConstructorParameters>[0]; -type Reject = Parameters>[0]>[1]; /** * @internal * This class is an abstraction over timeouts * The Timeout class can only be in the pending or rejected states. It is guaranteed not to resolve * if interacted with exclusively through its public API * */ -export class Timeout extends Promise { +export class Timeout implements PromiseLike { private id?: NodeJS.Timeout; public readonly start: number; @@ -39,6 +37,7 @@ export class Timeout extends Promise { public duration: number; private timedOut = false; public cleared = false; + private promise: Promise; get remainingTime(): number { if (this.timedOut) return 0; @@ -51,10 +50,7 @@ export class Timeout extends Promise { } /** Create a new timeout that expires in `duration` ms */ - private constructor( - executor: Executor = () => null, - options?: { duration: number; unref?: true; rejection?: Error } - ) { + private constructor(options?: { duration: number; unref?: true; rejection?: Error }) { const duration = options?.duration ?? 0; const unref = !!options?.unref; const rejection = options?.rejection; @@ -63,13 +59,8 @@ export class Timeout extends Promise { throw new MongoInvalidArgumentError('Cannot create a Timeout with a negative duration'); } - let reject!: Reject; - super((_, promiseReject) => { - reject = promiseReject; - - executor(noop, promiseReject); - }); - + const { promise, reject } = promiseWithResolvers(); + this.promise = promise; this.duration = duration; this.start = Math.trunc(performance.now()); @@ -90,6 +81,13 @@ export class Timeout extends Promise { } } + then<_TResult1 = never, TResult2 = never>( + _?: undefined, + onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null + ): PromiseLike { + return this.promise.then(undefined, onrejected as any); + } + /** * Clears the underlying timeout. This method is idempotent */ @@ -105,11 +103,11 @@ export class Timeout extends Promise { } public static expires(duration: number, unref?: true): Timeout { - return new Timeout(undefined, { duration, unref }); + return new Timeout({ duration, unref }); } - static override reject(rejection?: Error): Timeout { - return new Timeout(undefined, { duration: 0, unref: true, rejection }); + static reject(rejection?: Error): Timeout { + return new Timeout({ duration: 0, unref: true, rejection }); } }