Skip to content

Instantly share code, notes, and snippets.

@dbrockman
Created August 11, 2016 07:58
Show Gist options
  • Save dbrockman/0c65d859ba3ee531ce32a2b6fb624e24 to your computer and use it in GitHub Desktop.
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 :)
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;
}
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