'use strict';
var PENDING = 'pending'; var SETTLED = 'settled'; var FULFILLED = 'fulfilled'; var REJECTED = 'rejected'; var NOOP = function () {}; var isNode = typeof global !== 'undefined' && typeof global.process !== 'undefined' && typeof global.process.emit === 'function';
var asyncSetTimer = typeof setImmediate === 'undefined' ? setTimeout : setImmediate; 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) { 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; try { value = callback(value); } catch (e) { reject(promise, e); } } 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')) { // then should be retrieved only once var then = value.then; if (typeof then === 'function') { then.call(value, function (val) { if (!resolved) { resolved = true; if (value === val) { fulfill(promise, val); } else { resolve(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 = SETTLED; promise._data = value; asyncCall(publishFulfillment, promise); }
}
function reject(promise, reason) {
if (promise._state === PENDING) { promise._state = SETTLED; promise._data = reason; asyncCall(publishRejection, promise); }
}
function publish(promise) {
promise._then = promise._then.forEach(invokeCallback);
}
function publishFulfillment(promise) {
promise._state = FULFILLED; publish(promise);
}
function publishRejection(promise) {
promise._state = REJECTED; publish(promise); if (!promise._handled && isNode) { global.process.emit('unhandledRejection', promise._data, promise); }
}
function notifyRejectionHandled(promise) {
global.process.emit('rejectionHandled', promise);
}
/**
* @class */
function Promise(resolver) {
if (typeof resolver !== 'function') { throw new TypeError('Promise resolver ' + resolver + ' is not a function'); } if (this instanceof Promise === 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);
}
Promise.prototype = {
constructor: Promise, _state: PENDING, _then: null, _data: undefined, _handled: false, then: function (onFulfillment, onRejection) { var subscriber = { owner: this, then: new this.constructor(NOOP), fulfilled: onFulfillment, rejected: onRejection }; if ((onRejection || onFulfillment) && !this._handled) { this._handled = true; if (this._state === REJECTED && isNode) { asyncCall(notifyRejectionHandled, this); } } 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); }
};
Promise.all = function (promises) {
if (!Array.isArray(promises)) { throw new TypeError('You must pass an array to Promise.all().'); } return new Promise(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); } });
};
Promise.race = function (promises) {
if (!Array.isArray(promises)) { throw new TypeError('You must pass an array to Promise.race().'); } return new Promise(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); } } });
};
Promise.resolve = function (value) {
if (value && typeof value === 'object' && value.constructor === Promise) { return value; } return new Promise(function (resolve) { resolve(value); });
};
Promise.reject = function (reason) {
return new Promise(function (resolve, reject) { reject(reason); });
};
module.exports = Promise;