Skip to content

Instantly share code, notes, and snippets.

@TheCrafter
Last active September 11, 2019 09:51
Show Gist options
  • Save TheCrafter/418401b6ad59fa0190a6d9ab4d5f9fb2 to your computer and use it in GitHub Desktop.
Save TheCrafter/418401b6ad59fa0190a6d9ab4d5f9fb2 to your computer and use it in GitHub Desktop.
Typescript Promise that can be resolved/rejected externally
/**
* Created by Dimitris Vlachakis (TheCrafter) on 11/9/2019
* https://github.com/TheCrafter
* https://gist.github.com/TheCrafter/418401b6ad59fa0190a6d9ab4d5f9fb2
*
* Example: https://www.typescriptlang.org/play/?strictPropertyInitialization=false#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
*
* Simple wrapper to allow resolving a promise externally.
*/
// Util function to check for null or undefined values. Replace with your own.
function def(obj) {
return !(obj === null || obj === undefined);
}
/**
* A wrapper around Promise that allows to resolve/reject it externally. There is also
* a "reset" mechanism implemented so we don't have to re-create the OuterPromise object
* if we just want to re-use it.
* To start using it you need to either call reset() after creating the object or passing true
* as parameter for resetInConstructor when creating the object. That's because sometimes we may
* need to delay the instantiation of the promise until later.
* When the promise isn't created yet, the wait() function returns an empty resolved Promise.
*/
export class OuterPromise<T = any, E = any> {
private promise: Promise<T>; // will show as error without "strictPropertyInitialization": false in tsconfig.json
private outerResolve: (value?: T | PromiseLike<T>) => void = ()=>{};
private outerReject: (reason?: E) => void = ()=>{};
private _isPending = false;
private _isRejected = false;
private _isFulfilled = false;
constructor(resetInConstructor: boolean = false) {
if (resetInConstructor) {
this.reset();
}
}
public resolve(value?: T | PromiseLike<T>) {
if (def(this.promise)) {
this.outerResolve(value);
}
}
public reject(reason?: E) {
if (def(this.promise)) {
this.outerReject(reason);
}}
/**
* Returns the inside promise or an empty resolved promise if the inside promise doesn't exist yet.
* Also allows to set a timeout for auto-resolving the promise.
* Note:
* Doing "await myOuterPromise.wait(1000);" means that you will wait for the promise to EITHER be
* resolved on its own OR be auto resolved after 1000 msec.
*
* @param timeout Time in msec to wait before auto resolving the promise
* @param autoResolveValue A value to pass as argument when automatically resolving the promise
*/
public wait(timeout?: number, autoResolveValue?: T) : Promise<T> {
if (def(timeout) && def(this.promise)) {
setTimeout(() => {
this.resolve(autoResolveValue);
}, timeout);
}
return def(this.promise) ? this.promise : Promise.resolve({} as T);
}
public reset(): OuterPromise<T> {
this._isPending = true;
this._isFulfilled = false;
this._isRejected = false;
this.promise = new Promise<T>((resolve, reject) => {
this.outerResolve = resolve;
this.outerReject = reject;
});
this.promise
.then(() => {
this._isPending = false;
this._isFulfilled = true;
this._isRejected = false;
})
.catch(() => {
this._isPending = false;
this._isFulfilled = false;
this._isRejected = true;
});
return this;
}
get isPending(): boolean { return this._isPending; }
get isRejected(): boolean { return this._isRejected; }
get isFulfilled(): boolean { return this._isFulfilled; }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment