polyfill-service
Version:
A polyfill combinator
313 lines (285 loc) • 9.44 kB
JavaScript
/**
* Promise polyfill v1.0.10
* requires setImmediate
*
* © 2014–2015 Dmitry Korobkin
* Released under the MIT license
* github.com/Octane/Promise
*/
(function (global) {'use strict';
var STATUS = '[[PromiseStatus]]';
var VALUE = '[[PromiseValue]]';
var ON_FUlFILLED = '[[OnFulfilled]]';
var ON_REJECTED = '[[OnRejected]]';
var ORIGINAL_ERROR = '[[OriginalError]]';
var PENDING = 'pending';
var INTERNAL_PENDING = 'internal pending';
var FULFILLED = 'fulfilled';
var REJECTED = 'rejected';
var NOT_ARRAY = 'not an array.';
var REQUIRES_NEW = 'constructor Promise requires "new".';
var CHAINING_CYCLE = 'then() cannot return same Promise that it resolves.';
// Modifed by polyfill service - remove undefined require statement
var setImmediate = global.setImmediate;
var isArray = Array.isArray || function (anything) {
return Object.prototype.toString.call(anything) == '[object Array]';
};
function InternalError(originalError) {
this[ORIGINAL_ERROR] = originalError;
}
function isInternalError(anything) {
return anything instanceof InternalError;
}
function isObject(anything) {
//Object.create(null) instanceof Object → false
return Object(anything) === anything;
}
function isCallable(anything) {
return typeof anything == 'function';
}
function isPromise(anything) {
return anything instanceof Promise;
}
function identity(value) {
return value;
}
function thrower(reason) {
throw reason;
}
function enqueue(promise, onFulfilled, onRejected) {
if (!promise[ON_FUlFILLED]) {
promise[ON_FUlFILLED] = [];
promise[ON_REJECTED] = [];
}
promise[ON_FUlFILLED].push(onFulfilled);
promise[ON_REJECTED].push(onRejected);
}
function clearAllQueues(promise) {
delete promise[ON_FUlFILLED];
delete promise[ON_REJECTED];
}
function callEach(queue) {
var i;
var length = queue.length;
for (i = 0; i < length; i++) {
queue[i]();
}
}
function call(resolve, reject, value) {
var anything = toPromise(value);
if (isPromise(anything)) {
anything.then(resolve, reject);
} else if (isInternalError(anything)) {
reject(anything[ORIGINAL_ERROR]);
} else {
resolve(value);
}
}
function toPromise(anything) {
var then;
if (isPromise(anything)) {
return anything;
}
if(isObject(anything)) {
try {
then = anything.then;
} catch (error) {
return new InternalError(error);
}
if (isCallable(then)) {
return new Promise(function (resolve, reject) {
setImmediate(function () {
try {
then.call(anything, resolve, reject);
} catch (error) {
reject(error);
}
});
});
}
}
return null;
}
function resolvePromise(promise, resolver) {
function resolve(value) {
if (promise[STATUS] == PENDING) {
fulfillPromise(promise, value);
}
}
function reject(reason) {
if (promise[STATUS] == PENDING) {
rejectPromise(promise, reason);
}
}
try {
resolver(resolve, reject);
} catch(error) {
reject(error);
}
}
function fulfillPromise(promise, value) {
var queue;
var anything = toPromise(value);
if (isPromise(anything)) {
promise[STATUS] = INTERNAL_PENDING;
anything.then(
function (value) {
fulfillPromise(promise, value);
},
function (reason) {
rejectPromise(promise, reason);
}
);
} else if (isInternalError(anything)) {
rejectPromise(promise, anything[ORIGINAL_ERROR]);
} else {
promise[STATUS] = FULFILLED;
promise[VALUE] = value;
queue = promise[ON_FUlFILLED];
if (queue && queue.length) {
clearAllQueues(promise);
callEach(queue);
}
}
}
function rejectPromise(promise, reason) {
var queue = promise[ON_REJECTED];
promise[STATUS] = REJECTED;
promise[VALUE] = reason;
if (queue && queue.length) {
clearAllQueues(promise);
callEach(queue);
}
}
function Promise(resolver) {
var promise = this;
if (!isPromise(promise)) {
throw new TypeError(REQUIRES_NEW);
}
promise[STATUS] = PENDING;
promise[VALUE] = undefined;
resolvePromise(promise, resolver);
}
Promise.prototype.then = function (onFulfilled, onRejected) {
var promise = this;
var nextPromise;
onFulfilled = isCallable(onFulfilled) ? onFulfilled : identity;
onRejected = isCallable(onRejected) ? onRejected : thrower;
nextPromise = new Promise(function (resolve, reject) {
function tryCall(func) {
var value;
try {
value = func(promise[VALUE]);
} catch (error) {
reject(error);
return;
}
if (value === nextPromise) {
reject(new TypeError(CHAINING_CYCLE));
} else {
call(resolve, reject, value);
}
}
function asyncOnFulfilled() {
setImmediate(tryCall, onFulfilled);
}
function asyncOnRejected() {
setImmediate(tryCall, onRejected);
}
switch (promise[STATUS]) {
case FULFILLED:
asyncOnFulfilled();
break;
case REJECTED:
asyncOnRejected();
break;
default:
enqueue(promise, asyncOnFulfilled, asyncOnRejected);
}
});
return nextPromise;
};
Promise.prototype['catch'] = function (onRejected) {
return this.then(identity, onRejected);
};
Promise.resolve = function (value) {
var anything = toPromise(value);
if (isPromise(anything)) {
return anything;
}
return new Promise(function (resolve, reject) {
if (isInternalError(anything)) {
reject(anything[ORIGINAL_ERROR]);
} else {
resolve(value);
}
});
};
Promise.reject = function (reason) {
return new Promise(function (resolve, reject) {
reject(reason);
});
};
Promise.race = function (values) {
return new Promise(function (resolve, reject) {
var i;
var length;
if (isArray(values)) {
length = values.length;
for (i = 0; i < length; i++) {
call(resolve, reject, values[i]);
}
} else {
reject(new TypeError(NOT_ARRAY));
}
});
};
Promise.all = function (values) {
return new Promise(function (resolve, reject) {
var fulfilledCount = 0;
var promiseCount = 0;
var anything;
var length;
var value;
var i;
if (isArray(values)) {
values = values.slice(0);
length = values.length;
for (i = 0; i < length; i++) {
value = values[i];
anything = toPromise(value);
if (isPromise(anything)) {
promiseCount++;
anything.then(
function (index) {
return function (value) {
values[index] = value;
fulfilledCount++;
if (fulfilledCount == promiseCount) {
resolve(values);
}
};
}(i),
reject
);
} else if (isInternalError(anything)) {
reject(anything[ORIGINAL_ERROR]);
} else {
//[1, , 3] → [1, undefined, 3]
values[i] = value;
}
}
if (!promiseCount) {
resolve(values);
}
} else {
reject(new TypeError(NOT_ARRAY));
}
});
};
if (typeof module != 'undefined' && module.exports) {
module.exports = global.Promise || Promise;
} else if (!global.Promise) {
global.Promise = Promise;
}
}(this));