diff --git a/src/index.ts b/src/index.ts index 1f61417..663e10b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -29,6 +29,7 @@ export { SmartIterator, SmartAsyncIterator, SmartPromise, + Thenable, TimeoutException, TimedPromise, TypeException, diff --git a/src/models/index.ts b/src/models/index.ts index 8b7a69e..f1e451f 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -28,7 +28,7 @@ import GameLoop from "./game-loop.js"; export { SmartIterator, SmartAsyncIterator } from "./iterators/index.js"; export { JSONStorage } from "./json/index.js"; -export { DeferredPromise, SmartPromise, TimedPromise } from "./promises/index.js"; +export { DeferredPromise, SmartPromise, Thenable, TimedPromise } from "./promises/index.js"; import Publisher from "./publisher.js"; diff --git a/src/models/promises/thenable.ts b/src/models/promises/thenable.ts new file mode 100644 index 0000000..d6d53c3 --- /dev/null +++ b/src/models/promises/thenable.ts @@ -0,0 +1,97 @@ +export default class Thenable implements Promise +{ + protected _onFulfilled: (result: T) => T; + protected _resolve(result: T): T + { + return this._onFulfilled(result); + } + + public constructor() + { + this._onFulfilled = (result: T) => result; + } + + public then(onFulfilled?: null): Thenable; + public then(onFulfilled: (result: T) => F, onRejected?: null): Thenable; + public then(onFulfilled: (result: T) => F, onRejected: (reason: unknown) => R) + : Thenable; + public then(onFulfilled?: ((result: T) => F) | null, onRejected?: ((reason: unknown) => R) | null) + : Thenable + { + if (onRejected) + { + const _previousOnFulfilled = this._onFulfilled; + this._onFulfilled = (result: T) => + { + try + { + result = _previousOnFulfilled(result); + + return (onFulfilled!(result) as unknown) as T; + } + catch (error) + { + return (onRejected(error) as unknown) as T; + } + }; + } + else if (onFulfilled) + { + const _previousOnFulfilled = this._onFulfilled; + this._onFulfilled = (result: T) => + { + result = _previousOnFulfilled(result); + + return (onFulfilled(result) as unknown) as T; + }; + } + + return (this as unknown) as Thenable; + } + + public catch(onRejected?: null): Thenable; + public catch(onRejected: (reason: unknown) => R): Thenable; + public catch(onRejected?: ((reason: unknown) => R) | null): Thenable + { + if (onRejected) + { + const _previousOnFulfilled = this._onFulfilled; + this._onFulfilled = (result) => + { + try + { + return _previousOnFulfilled(result); + } + catch (error) + { + return (onRejected(error) as unknown) as T; + } + }; + } + + return this as Thenable; + } + + public finally(onFinally?: (() => void) | null): Thenable + { + if (onFinally) + { + const _previousOnFulfilled = this._onFulfilled; + this._onFulfilled = (result) => + { + try + { + return _previousOnFulfilled(result); + } + finally + { + onFinally(); + } + }; + } + + return this; + } + + public readonly [Symbol.toStringTag]: string = "Thenable"; +}