Skip to content

Instantly share code, notes, and snippets.

@the-fejw
Last active May 7, 2022 06:44
Show Gist options
  • Save the-fejw/3dedd101b0ef6655348cd842b343900d to your computer and use it in GitHub Desktop.
Save the-fejw/3dedd101b0ef6655348cd842b343900d to your computer and use it in GitHub Desktop.
const state = {
PENDING: 'pending',
FULFILLED: 'fulfilled',
REJECTED: 'rejected'
};
const isThenable = maybePromise => maybePromise && (typeof maybePromise === 'function');
class MyPromise {
constructor(executor) {
this._state = state.PENDING;
this._value = undefined;
this._errReason = undefined;
this._thenQueue = [];
if (typeof executor === 'function') {
setTimeout(() => {
try {
executor(
this._onFulfilled.bind(this),
this._onRejected.bind(this)
);
} catch (error) {
this._onRejected(error);
}
});
}
}
/** start external methods */
then(onFulfilled, onRejected) {
const controlledPromise = new MyPromise(onFulfilled, onRejected);
this._thenQueue.push([controlledPromise, onFulfilled, onRejected]);
if (this._state === state.FULFILLED) {
this._propagateFulfilled();
} else if (this._state === state.REJECTED) {
this._propagateRejected();
}
return controlledPromise;
}
catch(onRejected) {
return this.then(undefined, onRejected);
}
static resolve(value) {
return new MyPromise((resolve) => resolve(value));
}
static reject(value) {
return new MyPromise((_, reject) => reject(value));
}
/** end external methods */
/** start pseudo internal methods */
_onFulfilled(value) {
if (this._state === state.PENDING) {
this._state = state.FULFILLED;
this._value = value;
this._propagateFulfilled();
}
}
_onRejected(error) {
if (this._state === state.REJECTED) {
this._state = state.REJECTED;
this._errReason = error;
this._propagateRejected();
}
}
_propagateFulfilled() {
this._thenQueue.forEach(([controlledPromise, onFulfilled]) => {
if (typeof onFulfilled === 'function') {
const valueOrPromise = onFulfilled(this._value);
if (isThenable(valueOrPromise)) {
valueOrPromise.then(
value => controlledPromise._onFulfilled(value),
errorReason => controlledPromise._onRejected(errorReason)
);
} else {
controlledPromise._onFulfilled(valueOrPromise);
}
} else {
return controlledPromise._onFulfilled(this._value);
}
});
this._thenQueue = [];
}
_propagateRejected() {
this._thenQueue.forEach(([controlledPromise, _, onRejected]) => {
if (typeof onRejected === 'function') {
const valueOrPromise = onRejected(this._errReason);
if (isThenable(valueOrPromise)) {
valueOrPromise.then(
value => controlledPromise._onFulfilled(value),
errorReason => controlledPromise._onRejected(errorReason)
);
} else {
controlledPromise._onFulfilled(valueOrPromise);
}
} else {
return controlledPromise._onRejected(this._errReason);
}
});
}
/** end pseudo internal methods */
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment