Skip to content

Instantly share code, notes, and snippets.

@piyushgarg-dev
Created November 12, 2024 05:36
Show Gist options
  • Save piyushgarg-dev/44ccca631f0e48e1a7bef44711b5d832 to your computer and use it in GitHub Desktop.
Save piyushgarg-dev/44ccca631f0e48e1a7bef44711b5d832 to your computer and use it in GitHub Desktop.
Custom Promise in Typescript / Javascript
type TPromiseResolve<T> = (value: T) => void;
type TPromiseReject<T> = (reason: T) => void;
type TPromiseExecutor<T, K> = (
resolve: TPromiseResolve<T>,
reject: TPromiseReject<K>
) => void;
type TPromiseThenCallback<T> = (value: T | undefined) => void;
type TPromiseCatchCallback<T> = (reason: T | undefined) => void;
type TPromiseFinallyCallback = () => void;
enum PromiseState {
PENDING = 'pending',
FULFILLED = 'fulfilled',
REJECETD = 'rejected',
}
class MyPromise<T, K> {
private _state: PromiseState = PromiseState.PENDING;
private _successCallbackHandlers: TPromiseThenCallback<T>[] = [];
private _failureCallbackHandlers: TPromiseCatchCallback<K>[] = [];
private _finallyCallbackHandler: TPromiseFinallyCallback | undefined =
undefined;
private _value: T | undefined = undefined;
private _reason: K | undefined = undefined;
constructor(executor: TPromiseExecutor<T, K>) {
executor(
this._promiseResolver.bind(this),
this._promiseRejector.bind(this)
);
}
public then(handlerFn: TPromiseThenCallback<T>) {
if (this._state === PromiseState.FULFILLED) {
handlerFn(this._value);
} else {
this._successCallbackHandlers.push(handlerFn);
}
return this;
}
public catch(handlerFn: TPromiseCatchCallback<K>) {
if (this._state === PromiseState.REJECETD) {
handlerFn(this._reason);
} else {
this._failureCallbackHandlers.push(handlerFn);
}
return this;
}
public finally(handlerFn: TPromiseFinallyCallback) {
if (this._state !== PromiseState.PENDING) return handlerFn();
this._finallyCallbackHandler = handlerFn;
}
private _promiseResolver(value: T) {
if (this._state === PromiseState.FULFILLED) return;
this._state = PromiseState.FULFILLED;
this._value = value;
this._successCallbackHandlers.forEach((cb) => cb(value));
if (this._finallyCallbackHandler) this._finallyCallbackHandler();
}
private _promiseRejector(reason: K) {
if (this._state === PromiseState.REJECETD) return;
this._state = PromiseState.REJECETD;
this._reason = reason;
this._failureCallbackHandlers.forEach((cb) => cb(reason));
if (this._finallyCallbackHandler) this._finallyCallbackHandler();
}
}
@Ankit-Yadav-21
Copy link

type TPromiseResolve = (value: T) => void;
type TPromiseReject = (reason: T) => void;

type TPromiseExecutor<T, K> = (
resolve: TPromiseResolve,
reject: TPromiseReject
) => void;

type TPromiseThenCallback<T, TResult> = (value: T) => TResult | MyPromise<TResult, any>;
type TPromiseCatchCallback<K, TResult> = (reason: K) => TResult | MyPromise<TResult, any>;
type TPromiseFinallyCallback = () => void;

enum PromiseState {
PENDING = 'pending',
FULFILLED = 'fulfilled',
REJECTED = 'rejected',
}

class MyPromise<T, K> {
private _state: PromiseState = PromiseState.PENDING;

private _successCallbackHandlers: Array<TPromiseThenCallback<T, any>> = [];
private _failureCallbackHandlers: Array<TPromiseCatchCallback<K, any>> = [];
private _finallyCallbackHandler: TPromiseFinallyCallback | undefined = undefined;

private _value!: T;
private _reason!: K;

constructor(executor: TPromiseExecutor<T, K>) {
    executor(
        this._promiseResolver.bind(this),
        this._promiseRejector.bind(this)
    );
}

public then<TResult = T>(handlerFn: TPromiseThenCallback<T, TResult>): MyPromise<TResult, K> {
    return new MyPromise<TResult, K>((resolve, reject) => {
        const handleFulfilled = (value: T) => {
            try {
                const result = handlerFn(value);
                if (result instanceof MyPromise) {
                    result.then(resolve as TPromiseResolve<TResult>);
                } else {
                    resolve(result as unknown as TResult); // Use unknown as an intermediary
                }
            } catch (error) {
                reject(error as K);
            }
        };

        if (this._state === PromiseState.FULFILLED) {
            handleFulfilled(this._value);
        } else {
            this._successCallbackHandlers.push(handleFulfilled);
        }
    });
}

public catch<TResult = K>(handlerFn: TPromiseCatchCallback<K, TResult>): MyPromise<T, TResult> {
    return new MyPromise<T, TResult>((resolve, reject) => {
        const handleRejected = (reason: K) => {
            try {
                const result = handlerFn(reason);
                if (result instanceof MyPromise) {
                    result.then(reject as TPromiseReject<TResult>);
                } else {
                    resolve(result as unknown as T);
                }
            } catch (error) {
                reject(error as TResult);
            }
        };

        if (this._state === PromiseState.REJECTED) {
            handleRejected(this._reason);
        } else {
            this._failureCallbackHandlers.push(handleRejected);
        }
    });
}

public finally(handlerFn: TPromiseFinallyCallback): MyPromise<T, K> {
    if (this._state !== PromiseState.PENDING) {
        handlerFn();
    } else {
        this._finallyCallbackHandler = handlerFn;
    }
    return this;
}

private _promiseResolver(value: T) {
    if (this._state !== PromiseState.PENDING) return;
    this._state = PromiseState.FULFILLED;
    this._value = value;
    this._successCallbackHandlers.forEach((cb) => cb(value));
    if (this._finallyCallbackHandler) this._finallyCallbackHandler();
}

private _promiseRejector(reason: K) {
    if (this._state !== PromiseState.PENDING) return;
    this._state = PromiseState.REJECTED;
    this._reason = reason;
    this._failureCallbackHandlers.forEach((cb) => cb(reason));
    if (this._finallyCallbackHandler) this._finallyCallbackHandler();
}

}

Bro, I tried to make it and I learned some new things of classes.

Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment