Skip to content

Instantly share code, notes, and snippets.

@zenparsing
Created December 28, 2013 03:48
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 zenparsing/8155933 to your computer and use it in GitHub Desktop.
Save zenparsing/8155933 to your computer and use it in GitHub Desktop.
Minimal ES6 Promise
function enqueueMicrotask(fn) {
// Enqueue a function on the System-defined FIFO
// microtask queue.
}
var $status = "Promise#status",
$value = "Promise#value",
$onResolve = "Promise#onResolve",
$onReject = "Promise#onReject";
function isPromise(x) {
return x && $status in Object(x);
}
function isThenable(x) {
return x && "then" in Object(x) && typeof x.then === "function";
}
function promiseResolve(promise, x) {
promiseDone(promise, "resolved", x, promise[$onResolve]);
}
function promiseReject(promise, x) {
promiseDone(promise, "rejected", x, promise[$onReject]);
}
function promiseDone(promise, status, value, reactions) {
if (promise[$status] !== "pending")
return;
promise[$status] = status;
promise[$value] = value;
promise[$onResolve] = promise[$onReject] = void 0;
for (var i = 0; i < reactions.length; ++i)
promiseReact(reactions[i][0], reactions[i][1], value);
}
function promiseUnwrap(deferred, x) {
if (x === deferred.promise)
throw new TypeError;
if (isPromise(x))
x.chain(deferred.resolve, deferred.reject);
else
deferred.resolve(x);
}
function promiseReact(deferred, handler, x) {
enqueueMicrotask($=> {
try { promiseUnwrap(deferred, handler(x)) }
catch(e) { deferred.reject(e) }
});
}
function getDeferred(constructor) {
var result = {};
result.promise = new constructor((resolve, reject) => {
result.resolve = resolve;
result.reject = reject;
});
return result;
}
class Promise {
constructor(init) {
if (typeof init !== "function")
throw new TypeError("Promise constructor called without initializer");
this[$status] = "pending";
this[$onResolve] = [];
this[$onReject] = [];
init(x => promiseResolve(this, x), r => promiseReject(this, r));
}
// "chain" is the primative unwrapping operation
chain(onResolve, onReject) {
if (typeof onResolve !== "function") onResolve = x => x;
if (typeof onReject !== "function") onReject = e => { throw e };
var deferred = getDeferred(this.constructor);
switch (this[$status]) {
case undefined:
throw new TypeError;
case "pending":
this[$onResolve].push([deferred, onResolve]);
this[$onReject].push([deferred, onReject]);
break;
case "resolved":
promiseReact(deferred, onResolve, this[$value]);
break;
case "rejected":
promiseReact(deferred, onReject, this[$value]);
break;
}
return deferred.promise;
}
// "then" is described in terms of "chain"
then(onResolve, onReject) {
if (typeof onResolve !== "function") onResolve = x => x;
return this.chain(x => {
if (x === this)
throw new TypeError;
return isThenable(x) ?
x.then(onResolve, onReject) :
onResolve(x);
}, onReject);
}
// Base-level convenience
static resolve(x) {
return new this(resolve => resolve(x));
}
// Base-level convenience
static reject(x) {
return new this((resolve, reject) => reject(x));
}
// Nominal type test is essential for impementing when, etc.
static isPromise(x) {
return isPromise(x);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment