UNPKG

@byloth/core

Version:

An unopinionated collection of useful functions and classes that I use widely in all my projects. 🔧

132 lines (118 loc) • 4.8 kB
import type { PromiseResolver, PromiseRejecter, FulfilledHandler, RejectedHandler } from "./types.js"; import SmartPromise from "./smart-promise.js"; /** * A class representing a {@link SmartPromise} that can be resolved or rejected from the "outside". * The `resolve` and `reject` methods are exposed to allow the promise to be settled from another context. * * It's particularly useful in scenarios where the promise is created and needs to be awaited in one place, * while being resolved or rejected in another (e.g. an event handler for an user interaction). * * This is a change in the approach to promises: instead of defining how the promise will be resolved (or rejected), * you define how to handle the resolution (or rejection) when it occurs. * * --- * * @example * ```ts * const promise = new DeferredPromise<string, string[]>((value: string) => value.split(" ")); * * promise.then((result) => console.log(result)); // ["Hello,", "World!"] * promise.resolve("Hello, World!"); * ``` * * --- * * @template T The type of value the promise expects to initially be resolved with. Default is `void`. * @template F * The type of value returned by the `onFulfilled` callback. * This will be the actual type of value the promise will eventually resolve to. Default is `T`. * @template R * The type of value possibly returned by the `onRejected` callback. * This will be coupled with the type of value the promise will eventually resolve to, if provided. Default is `never`. */ export default class DeferredPromise<T = void, F = T, R = never> extends SmartPromise<F | R> { /** * The exposed function that allows to resolve the promise. * * This protected property is the only one that can be modified directly by the derived classes. * If you're looking for the public and readonly property, use the {@link DeferredPromise.resolve} getter instead. */ protected readonly _resolve: PromiseResolver<T>; /** * The exposed function that allows to reject the promise. */ public get resolve(): PromiseResolver<T> { return this._resolve; } /** * The exposed function that allows to reject the promise. * * This protected property is the only one that can be modified directly by the derived classes. * If you're looking for the public and readonly property, use the {@link DeferredPromise.reject} getter instead. */ protected readonly _reject: PromiseRejecter; /** * The exposed function that allows to reject the promise. */ public get reject(): PromiseRejecter { return this._reject; } /** * The native {@link Promise} object wrapped by this instance. */ declare protected readonly _promise: Promise<F | R>; /** * Initializes a new instance of the {@link DeferredPromise} class. * * --- * * @example * ```ts * const promise = new DeferredPromise<string, string[]>((value: string) => value.split(" ")); * ``` * * --- * * @param onFulfilled The callback to execute once the promise is fulfilled. * @param onRejected The callback to execute once the promise is rejected. */ public constructor(onFulfilled?: FulfilledHandler<T, F> | null, onRejected?: RejectedHandler<unknown, R> | null) { let _resolve: PromiseResolver<T>; let _reject: PromiseRejecter; super((resolve, reject) => { // ReferenceError: Must call super constructor in derived class before accessing // 'this' or returning from derived constructor. // _resolve = resolve as PromiseResolver<T>; _reject = reject; }); this._promise = this._promise.then(onFulfilled as FulfilledHandler<F | R>, onRejected); this._resolve = _resolve!; this._reject = _reject!; } /** * Watches another promise and resolves or rejects this promise when the other one is settled. * * --- * * @example * ```ts * const promise = new Promise<string>((resolve) => setTimeout(() => resolve("Hello, World!"), 1_000)); * const deferred = new DeferredPromise<string, string[]>((value: string) => value.split(" ")); * * deferred.then((result) => console.log(result)); // ["Hello,", "World!"] * deferred.watch(promise); * ``` * * --- * * @param otherPromise The promise to watch. * * @returns The current instance of the {@link DeferredPromise} class. */ public watch(otherPromise: PromiseLike<T>): this { otherPromise.then(this.resolve, this.reject); return this; } public override readonly [Symbol.toStringTag]: string = "DeferredPromise"; }