Skip to content

Instantly share code, notes, and snippets.

@killroy42
Created July 1, 2019 11:27
Show Gist options
  • Save killroy42/d319e8d323a099836b8c5149c1ae7029 to your computer and use it in GitHub Desktop.
Save killroy42/d319e8d323a099836b8c5149c1ae7029 to your computer and use it in GitHub Desktop.
Minimal, tolerant and feature-rich promise polyfill
(function(scope) {
var Fcb = Function.call.bind, Apt = Array.prototype;
var slice = Fcb(Apt.slice);
var concat = Fcb(Apt.concat);
var forEach = Fcb(Apt.forEach);
//var setT = XS['set_Timeout'];
// async
var queue = [];
function async(callback) {
queue.push(callback);
if(queue.length === 1) async.async();
};
async.run = function() {
while(queue.length) {
queue[0]();
queue.shift();
}
};
async.async = function() {
setT(async.run);
};
// Promise
var P = function Promise(executor) {
this.state = P.State.PENDING;
this.value = undefined;
this.deferred = [];
var promise = this;
try {
executor(
function(x) { promise.resolve(x); },
function (r) { promise.reject(r); }
);
} catch (e) {
promise.reject(e);
}
};
P.State = {
RESOLVED: 0,
REJECTED: 1,
PENDING: 2
};
P.reject = function(r) {
return new P(function (resolve, reject) {
reject(r);
});
};
P.resolve = function(x) {
return new P(function (resolve, reject) {
resolve(x);
});
};
P.all = function all(iterable) {
return new P(function(resolve, reject) {
var count = 0, result = [];
if(iterable.length === 0) resolve(result);
function resolver(i) {
return function(x) {
result[i] = x;
count += 1;
if (count === iterable.length) resolve(result);
};
}
for(var i = 0; i < iterable.length; i += 1) {
P.resolve(iterable[i]).then(resolver(i), reject);
}
});
};
P.race = function race(iterable) {
return new P(function(resolve, reject) {
for(var i = 0; i < iterable.length; i += 1) {
P.resolve(iterable[i]).then(resolve, reject);
}
});
};
var pt = P.prototype;
pt.resolve = function resolve(x) {
var promise = this;
if(promise.state === P.State.PENDING) {
if(x === promise) throw new TypeError();
var called = false;
try {
var then = x && x['then'];
if(x != null && typeof x === 'object' && typeof then === 'function') {
then.call(x,
function(x) {
if(!called) promise.resolve(x);
called = true;
},
function(r) {
if(!called) promise.reject(r);
called = true;
}
);
return;
}
}
catch (e) {
if(!called) promise.reject(e);
return;
}
promise.state = P.State.RESOLVED;
promise.value = x;
promise.notify();
}
};
pt.reject = function reject(reason) {
var promise = this;
if(promise.state === P.State.PENDING) {
if(reason === promise) throw new TypeError();
promise.state = P.State.REJECTED;
promise.value = reason;
promise.notify();
}
};
pt.notify = function notify() {
var promise = this;
async(function() {
if(promise.state != P.State.PENDING) {
while(promise.deferred.length) {
var deferred = promise.deferred.shift(),
onResolved = deferred[0],
onRejected = deferred[1],
resolve = deferred[2],
reject = deferred[3];
try {
if(promise.state === P.State.RESOLVED) {
if(typeof onResolved === 'function') resolve(onResolved.call(undefined, promise.value));
else resolve(promise.value);
} else if(promise.state === P.State.REJECTED) {
if(typeof onRejected === 'function') resolve(onRejected.call(undefined, promise.value));
else reject(promise.value);
}
}
catch (e) {
reject(e);
}
}
}
});
};
pt.catch = function(onRejected) {
return this.then(undefined, onRejected);
};
pt.then = function then(onResolved, onRejected) {
var promise = this;
return new P(function(resolve, reject) {
promise.deferred.push([onResolved, onRejected, resolve, reject]);
promise.notify();
});
};
pt.finally = function finally(callback) {
function handler(res) {
return P.resolve(callback())
.then(function() {
return res;
});
}
return this
.then(handler, handler);
};
if(scope.Promise === undefined) scope.Promise = P;
function promisify(orig) {
var promisified = function fn() {
var args = slice(arguments);
var self = this;
return new Promise(function(resolve, reject) {
orig.apply(self, concat(args, function(err) {
var res = arguments.length > 1 ? slice(arguments, 1) : [];
if(err) reject(err);
else if(res.length > 1) resolve(res);
else resolve(res[0]);
}));
});
};
return promisified;
};
if(scope.Promise.promisify === undefined) {
scope.Promise.promisify = promisify;
}
})(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment