Skip to content

Instantly share code, notes, and snippets.

@kmontg
Created May 20, 2017 01:28
Show Gist options
  • Save kmontg/b96b7950e75183b5df99f58420b4f016 to your computer and use it in GitHub Desktop.
Save kmontg/b96b7950e75183b5df99f58420b4f016 to your computer and use it in GitHub Desktop.
Promise Implementation
'use strict';
let [PENDING, FULFILLED, REJECTED] = [0, 1, 2];
let pn = 0;
let tick = 0;
console.log('Executing Main Script...');
let clr = setInterval(() => {
console.log('TICK #' + (++tick) + '...');
}, 0);
function Promise(fn) {
// internal state of current promise instance
let state = PENDING;
let value = null;
let handlers = [];
let _pn = ++pn;
console.log('Creating Promise p' + _pn);
function fulfill(result) {
console.log('Fulfilling p' + _pn + ' with ' + result);
state = FULFILLED;
value = result;
handlers.forEach(handle);
handlers = null;
}
function reject(error) {
console.log('Rejecting p' + _pn + ' with' + error);
state = REJECTED;
value = error;
handlers.forEach(handle);
handlers = null;
}
function handle(handler) {
if (state === PENDING) {
handlers.push(handler);
} else {
if (state === FULFILLED && typeof handler.onFulfilled === 'function') {
handler.onFulfilled(value);
}
if (state === REJECTED && typeof handler.onRejected === 'function') {
handler.onRejected(value);
}
}
}
function getThen(value) {
let t = typeof value;
if (value && (t === 'object' || t === 'function')) {
let then = value.then;
if (typeof then === 'function') {
return then;
}
}
return null;
}
/*
* Wrap misbehaving resolver function to ensure onFulfilled and onRejected are only called once
*/
function doResolve(fn, onFulfilled, onRejected) {
let done = false;
try {
let _onFulfilled = (value) => {
if (done) return;
done = true;
onFulfilled(value);
};
let _onRejected = (reason) => {
if (done) return;
done = true;
onRejected(value);
};
fn(_onFulfilled, _onRejected);
} catch (ex) {
if (done) return;
done = true;
onRejected(ex);
}
}
function resolve(result) {
console.log('Resolving p' + _pn);
try {
let then = getThen(result);
if (then) {
doResolve(then.bind(result), resolve, reject);
return;
}
fulfill(result);
} catch (ex) {
reject(ex);
}
}
this.done = function(onFulfilled, onRejected) {
console.log('Registering done() callbacks for p' + _pn);
setTimeout(() => {
handle({
onFulfilled,
onRejected,
});
}, 0);
}
this.then = function(onFulfilled, onRejected) {
console.log('Calling then() on p' + _pn);
/*
* Store a reference to the 'outer' Promise
*/
let self = this;
/*
* Create and return an 'inner' Promise that is linked (via 'self') to the 'outer' Promise
*/
return new Promise(function(resolve, reject){
/*
* When the outer promise resolves, trigger the process for resolving the 'inner' Promise.
* If 'onFulfilled' or 'onRejected' were passed as functions, they should be called and their results
* used accordingly to resolve the 'inner' Promise. The process for resolving the inner promise with
* a thenable can be quite complex to trace. The revealing constructor pattern used here adds to the complexity
* as the closure that holds the resolve and reject functions must be carefully followed to know which promise is
* being resolved.
*/
self.done(
(result) => {
if (typeof onFulfilled === 'function') {
try {
resolve(onFulfilled(result));
} catch (ex) {
reject(ex);
}
} else {
resolve(result);
}
},
(error) => {
if (typeof onRejected === 'function') {
try {
resolve(onRejected(error));
} catch (ex) {
reject(ex);
}
} else {
reject(error);
}
}
);
});
}
doResolve(fn, resolve, reject);
}
// Case 1: Resolving with a Promise
// let p1 = new Promise((resolve, reject) => {
// resolve(10);
// });
// let p2 = new Promise((resolve, reject) => {
// resolve(p1);
// });
// p2.done((val) => {
// console.log(val);
// clearInterval(clr);
// });
// Case 2: Calling then() on Promise and returning another Promise from onFulfillment handler
// new Promise((resolve, reject) => {
// resolve(10);
// }).then((val) => {
// console.log(val);
// return new Promise((resolve, reject) => {
// resolve(val*10);
// });
// }).done((val) => {
// console.log(val);
// clearInterval(clr);
// });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment