Skip to content

Instantly share code, notes, and snippets.

@phamtm
Last active January 2, 2017 09:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save phamtm/af611d927cf2d51b5d43eecefb70d0b3 to your computer and use it in GitHub Desktop.
Save phamtm/af611d927cf2d51b5d43eecefb70d0b3 to your computer and use it in GitHub Desktop.
Promise - Simple implementation

Promise implementation

  • Only the original promise (created with new) can control when it will resolve/reject (callee decide)

  • Each subsequent promise (subscriber) created with then will either resolve when the onFulfilled and onRejected return OR if no handler resolve with the same value as the parent

  • A promise should NEVER be resolved with another promise.

Who wait for whom?

  • Based on callbacks: p0 = resolve(p1) => resolve(p2) => ... => resolve(pn) then p0 must wait until pn resolves. p0 .. p(n-1) with resolve with same value as pn

  • p1 = p0.then() p2 = p0.then() ... pn = p0.then()

In this case, p1...pn must wait until p0 resolved.

Examples

  • p1 => resolve(p2)
  • p1 => resolve(42).then(42 => return 42 + 1)
/**
* @see http://stackoverflow.com/questions/23772801/basic-javascript-promise-implementation-attempt/23785244#23785244
* @see https://www.promisejs.org/implementing/
*/
const Pending = 0;
const Fulfilled = 1;
const Rejected = 2;
function Promise(unsafeResolver) {
let state = Pending;
let value = null;
let thens = []; // opt
doResolve(unsafeResolver, resolve, reject);
// transition
function fulfill(result) {
state = Fulfilled;
value = result;
thens.forEach(handleThen); // opt
thens = null;
}
function reject(result) {
state = Rejected;
value = result;
thens.forEach(handleThen); // opt
thens = null;
}
// callback to resolve our promise. Trigger by some external script
function resolve(result) {
try {
const then = getThen(result);
then
? doResolve(then.bind(result), resolve, reject)
: fulfill(result);
} catch (e) {
reject(e);
}
}
/**
* Resolve the parent promise with the value of child promise
*
* @param unsafeResolver user provider resolver function, could potentially try to resolve more than once
* @param onFulfilled to resolve parent promise
* @param onRejected to reject parent promise
*/
function doResolve(unsafeResolver, onFulfilled, onRejected) {
let done = false;
const onFulfilledOnce = value => {
if (done) return;
done = true;
onFulfilled(value);
};
const onRejectedOnce = value => {
if (done) return;
done = true;
onRejected(value);
};
try {
unsafeResolver(onFulfilledOnce, onRejectedOnce);
} catch (e) {
onRejectedOnce(e); // opt: inline
}
}
function handleThen(thenHandler) { // opt: don't create object
// Resolve callback (created via `.done` that has been waiting for this promise
// to resolve
if (state === Pending) {
thens.push(thenHandler);
} else if (state === Fulfilled && typeof thenHandler.onFulfilled === 'function') {
thenHandler.onFulfilled(value);
} else if (state === Rejected && typeof thenHandler.onRejected === 'function') {
thenHandler.onRejected(value);
}
}
// Observing via done, `onFulfilled` and `onRejected` must be invoked after
// `done` is finished
function done(onFulfilled, onRejected) {
setTimeout(() => handleThen({ onFulfilled, onRejected }), 0);
}
this.then = (onFulfilled, onRejected) => {
const self = this;
return new Promise((resolve, reject) =>
self.done(
value => {
if (typeof onFulfilled === 'function') {
try {
resolve(onFulfilled(value));
} catch (e) {
reject(e);
}
} else {
resolve(value);
}
},
value => {
if (typeof onRejected === 'function') {
try {
resolve(onRejected(value));
} catch (e) {
reject(e);
}
} else {
reject(value);
}
}
)
);
};
}
function getThen(promise) {
if (promise && (typeof promise === 'function' || typeof promise === 'object')) {
const then = promise.then;
if (typeof then === 'function')
return then;
}
return null;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment