Created
May 20, 2017 01:28
-
-
Save kmontg/b96b7950e75183b5df99f58420b4f016 to your computer and use it in GitHub Desktop.
Promise Implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'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