Created
July 1, 2019 11:27
-
-
Save killroy42/d319e8d323a099836b8c5149c1ae7029 to your computer and use it in GitHub Desktop.
Minimal, tolerant and feature-rich promise polyfill
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
(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