Created
February 24, 2017 15:43
-
-
Save chemzqm/8e50e23c35cc14992831027a40b8d871 to your computer and use it in GitHub Desktop.
miniapp promise
This file contains 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
var PENDING = 'pending' | |
var SEALED = 'sealed' | |
var FULFILLED = 'fulfilled' | |
var REJECTED = 'rejected' | |
var NOOP = function(){} | |
function isArray(value) { | |
return Object.prototype.toString.call(value) === '[object Array]' | |
} | |
// async calls | |
var asyncSetTimer = typeof setImmediate !== 'undefined' ? setImmediate : setTimeout | |
var asyncQueue = [] | |
var asyncTimer | |
function asyncFlush(){ | |
// run promise callbacks | |
for (var i = 0; i < asyncQueue.length; i++) | |
asyncQueue[i][0](asyncQueue[i][1]) | |
// reset async asyncQueue | |
asyncQueue = [] | |
asyncTimer = false | |
} | |
function asyncCall(callback, arg){ | |
asyncQueue.push([callback, arg]) | |
if (!asyncTimer) | |
{ | |
asyncTimer = true | |
asyncSetTimer(asyncFlush, 0) | |
} | |
} | |
function invokeResolver(resolver, promise) { | |
function resolvePromise(value) { | |
resolve(promise, value) | |
} | |
function rejectPromise(reason) { | |
reject(promise, reason) | |
} | |
try { | |
resolver(resolvePromise, rejectPromise) | |
} catch(e) { | |
console.log(e.stack) | |
rejectPromise(e) | |
} | |
} | |
function invokeCallback(subscriber){ | |
var owner = subscriber.owner | |
var settled = owner.state_ | |
var value = owner.data_ | |
var callback = subscriber[settled] | |
var promise = subscriber.then | |
if (typeof callback === 'function') | |
{ | |
settled = FULFILLED | |
value = callback(value) | |
} | |
if (!handleThenable(promise, value)) | |
{ | |
if (settled === FULFILLED) | |
resolve(promise, value) | |
if (settled === REJECTED) | |
reject(promise, value) | |
} | |
} | |
function handleThenable(promise, value) { | |
var resolved | |
try { | |
if (promise === value) | |
throw new TypeError('A promises callback cannot return that same promise.') | |
if (value && (typeof value === 'function' || typeof value === 'object')) | |
{ | |
var then = value.then; // then should be retrived only once | |
if (typeof then === 'function') | |
{ | |
then.call(value, function(val){ | |
if (!resolved) | |
{ | |
resolved = true | |
if (value !== val) | |
resolve(promise, val) | |
else | |
fulfill(promise, val) | |
} | |
}, function(reason){ | |
if (!resolved) | |
{ | |
resolved = true | |
reject(promise, reason) | |
} | |
}) | |
return true | |
} | |
} | |
} catch (e) { | |
if (!resolved) | |
reject(promise, e) | |
return true | |
} | |
return false | |
} | |
function resolve(promise, value){ | |
if (promise === value || !handleThenable(promise, value)) | |
fulfill(promise, value) | |
} | |
function fulfill(promise, value){ | |
if (promise.state_ === PENDING) | |
{ | |
promise.state_ = SEALED | |
promise.data_ = value | |
asyncCall(publishFulfillment, promise) | |
} | |
} | |
function reject(promise, reason){ | |
if (promise.state_ === PENDING) | |
{ | |
promise.state_ = SEALED | |
promise.data_ = reason | |
asyncCall(publishRejection, promise) | |
} | |
} | |
function publish(promise) { | |
var callbacks = promise.then_ | |
promise.then_ = undefined | |
for (var i = 0; i < callbacks.length; i++) { | |
invokeCallback(callbacks[i]) | |
} | |
} | |
function publishFulfillment(promise){ | |
promise.state_ = FULFILLED | |
publish(promise) | |
} | |
function publishRejection(promise){ | |
promise.state_ = REJECTED | |
publish(promise) | |
} | |
/** | |
* @class | |
*/ | |
function P(resolver){ | |
if (typeof resolver !== 'function') | |
throw new TypeError('Promise constructor takes a function argument') | |
if (this instanceof P === false) | |
throw new TypeError('Failed to construct \'Promise\': Please use the \'new\' operator, this object constructor cannot be called as a function.') | |
this.then_ = [] | |
invokeResolver(resolver, this) | |
} | |
P.prototype = { | |
constructor: P, | |
state_: PENDING, | |
then_: null, | |
data_: undefined, | |
then: function(onFulfillment, onRejection){ | |
var subscriber = { | |
owner: this, | |
then: new this.constructor(NOOP), | |
fulfilled: onFulfillment, | |
rejected: onRejection | |
} | |
if (this.state_ === FULFILLED || this.state_ === REJECTED) | |
{ | |
// already resolved, call callback async | |
asyncCall(invokeCallback, subscriber) | |
} | |
else | |
{ | |
// subscribe | |
this.then_.push(subscriber) | |
} | |
return subscriber.then | |
}, | |
'catch': function(onRejection) { | |
return this.then(null, onRejection) | |
} | |
} | |
P.all = function(promises){ | |
var Class = this | |
if (!isArray(promises)) | |
throw new TypeError('You must pass an array to P.all().') | |
return new Class(function(resolve, reject){ | |
var results = [] | |
var remaining = 0 | |
function resolver(index){ | |
remaining++ | |
return function(value){ | |
results[index] = value | |
if (!--remaining) | |
resolve(results) | |
} | |
} | |
for (var i = 0, promise; i < promises.length; i++) | |
{ | |
promise = promises[i] | |
if (promise && typeof promise.then === 'function') | |
promise.then(resolver(i), reject) | |
else | |
results[i] = promise | |
} | |
if (!remaining) | |
resolve(results) | |
}) | |
} | |
P.race = function(promises){ | |
var Class = this | |
if (!isArray(promises)) | |
throw new TypeError('You must pass an array to P.race().') | |
return new Class(function(resolve, reject) { | |
for (var i = 0, promise; i < promises.length; i++) | |
{ | |
promise = promises[i] | |
if (promise && typeof promise.then === 'function') | |
promise.then(resolve, reject) | |
else | |
resolve(promise) | |
} | |
}) | |
} | |
P.resolve = function(value){ | |
var Class = this | |
if (value && typeof value === 'object' && value.constructor === Class) | |
return value | |
return new Class(function(resolve){ | |
resolve(value) | |
}) | |
} | |
P.reject = function(reason){ | |
var Class = this | |
return new Class(function(resolve, reject){ | |
reject(reason) | |
}) | |
} | |
module.exports = typeof Promise == 'function' ? Promise : P |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment