Created
August 11, 2016 07:58
-
-
Save dbrockman/0c65d859ba3ee531ce32a2b6fb624e24 to your computer and use it in GitHub Desktop.
Untested Promise implementation that I wrote just for fun. Almost certainly completely broken :)
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
import Queue from './queue'; | |
const STATE_PENDING = 'pending'; | |
const STATE_RESOLVED = 'resolved'; | |
const STATE_REJECTED = 'rejected'; | |
const states = new WeakMap(); | |
const values = new WeakMap(); | |
const queues = new WeakMap(); | |
let empty_resolved = null; | |
let empty_rejected = null; | |
export default class Promise { | |
constructor(fn) { | |
states.set(this, STATE_PENDING); | |
try { | |
fn(val => { | |
realize(this, STATE_RESOLVED, val); | |
}, err => { | |
realize(this, STATE_REJECTED, err); | |
}); | |
} | |
catch(ex) { | |
realize(this, STATE_REJECTED, ex); | |
} | |
} | |
then(vf, ef) { | |
if (!vf && !ef) { | |
return this; | |
} | |
const state = states.get(this); | |
if (state !== STATE_PENDING) { | |
const value = values.get(this); | |
const fn = state == STATE_RESOLVED ? vf : state == STATE_REJECTED ? ef : null; | |
if (!fn) { | |
return this; | |
} | |
return new Promise((resolve, reject) => { | |
fulfill(resolve, reject, fn, value); | |
}); | |
} | |
if (!queues.has(this)) { | |
queues.set(this, new Queue()); | |
} | |
const queue = queues.get(this); | |
if (vf) { | |
return new Promise((resolve, reject) => { | |
queue.push({ | |
state: STATE_RESOLVED, | |
fn: v => fulfill(resolve, reject, vf, v) | |
}); | |
}); | |
} | |
if (ef) { | |
return new Promise((resolve, reject) => { | |
queue.push({ | |
state: STATE_REJECTED, | |
fn: v => fulfill(resolve, reject, ef, v) | |
}); | |
}); | |
} | |
return this; | |
} | |
catch(ef) { | |
return this.then(null, ef); | |
} | |
static resolve(val) { | |
if (val === undefined) { | |
if (!empty_resolved) { | |
empty_resolved = new Promise(resolve => resolve()); | |
} | |
return empty_resolved; | |
} | |
return new Promise(resolve => resolve(val)); | |
} | |
static reject(err) { | |
if (err === undefined) { | |
if (!empty_rejected) { | |
empty_rejected = new Promise((resolve, reject) => reject()); | |
} | |
return empty_rejected; | |
} | |
return new Promise((resolve, reject) => reject(err)); | |
} | |
} | |
function realize(promise, state, value) { | |
if (states.get(promise) !== STATE_PENDING) { | |
return; | |
} | |
states.set(promise, state); | |
if (value !== undefined) { | |
values.set(promise, value); | |
} | |
const queue = queues.get(promise); | |
if (!queue) { | |
return; | |
} | |
while (!queue.isEmpty()) { | |
const item = queue.shift(); | |
if (item.state === state) { | |
(0, item.fn)(value); | |
} | |
} | |
queues.delete(promise); | |
} | |
function fulfill(resolve, reject, fn, val) { | |
let ret; | |
try { | |
ret = fn(val); | |
} | |
catch(err) { | |
setImmediate(reject, err); | |
return; | |
} | |
if (!ret || typeof ret.then !== 'function') { | |
setImmediate(resolve, ret); | |
return; | |
} | |
let sync = true; | |
ret.then(v => { | |
if (sync) { | |
setImmediate(resolve, v); | |
} | |
else { | |
resolve(v); | |
} | |
}, e => { | |
if (sync) { | |
setImmediate(reject, e); | |
} | |
else { | |
reject(e); | |
} | |
}); | |
sync = false; | |
} |
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
export default class Queue { | |
constructor() { | |
this.first = null; | |
this.last = null; | |
} | |
push(value) { | |
const node = { value: value, next: null }; | |
if (this.last) { | |
this.last.next = node; | |
} | |
else { | |
this.first = node; | |
} | |
this.last = node; | |
} | |
shift() { | |
var node = this.first; | |
if (!node) { | |
return undefined; | |
} | |
this.first = node.next; | |
if (!this.first) { | |
this.last = null; | |
} | |
return node.value; | |
} | |
isEmpty() { | |
return !this.first; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment