123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- 'use strict';
- var asap = require('asap/raw');
- function noop() {}
- // States:
- //
- // 0 - pending
- // 1 - fulfilled with _value
- // 2 - rejected with _value
- // 3 - adopted the state of another promise, _value
- //
- // once the state is no longer pending (0) it is immutable
- // All `_` prefixed properties will be reduced to `_{random number}`
- // at build time to obfuscate them and discourage their use.
- // We don't use symbols or Object.defineProperty to fully hide them
- // because the performance isn't good enough.
- // to avoid using try/catch inside critical functions, we
- // extract them to here.
- var LAST_ERROR = null;
- var IS_ERROR = {};
- function getThen(obj) {
- try {
- return obj.then;
- } catch (ex) {
- LAST_ERROR = ex;
- return IS_ERROR;
- }
- }
- function tryCallOne(fn, a) {
- try {
- return fn(a);
- } catch (ex) {
- LAST_ERROR = ex;
- return IS_ERROR;
- }
- }
- function tryCallTwo(fn, a, b) {
- try {
- fn(a, b);
- } catch (ex) {
- LAST_ERROR = ex;
- return IS_ERROR;
- }
- }
- module.exports = Promise;
- function Promise(fn) {
- if (typeof this !== 'object') {
- throw new TypeError('Promises must be constructed via new');
- }
- if (typeof fn !== 'function') {
- throw new TypeError('Promise constructor\'s argument is not a function');
- }
- this._U = 0;
- this._V = 0;
- this._W = null;
- this._X = null;
- if (fn === noop) return;
- doResolve(fn, this);
- }
- Promise._Y = null;
- Promise._Z = null;
- Promise._0 = noop;
- Promise.prototype.then = function(onFulfilled, onRejected) {
- if (this.constructor !== Promise) {
- return safeThen(this, onFulfilled, onRejected);
- }
- var res = new Promise(noop);
- handle(this, new Handler(onFulfilled, onRejected, res));
- return res;
- };
- function safeThen(self, onFulfilled, onRejected) {
- return new self.constructor(function (resolve, reject) {
- var res = new Promise(noop);
- res.then(resolve, reject);
- handle(self, new Handler(onFulfilled, onRejected, res));
- });
- }
- function handle(self, deferred) {
- while (self._V === 3) {
- self = self._W;
- }
- if (Promise._Y) {
- Promise._Y(self);
- }
- if (self._V === 0) {
- if (self._U === 0) {
- self._U = 1;
- self._X = deferred;
- return;
- }
- if (self._U === 1) {
- self._U = 2;
- self._X = [self._X, deferred];
- return;
- }
- self._X.push(deferred);
- return;
- }
- handleResolved(self, deferred);
- }
- function handleResolved(self, deferred) {
- asap(function() {
- var cb = self._V === 1 ? deferred.onFulfilled : deferred.onRejected;
- if (cb === null) {
- if (self._V === 1) {
- resolve(deferred.promise, self._W);
- } else {
- reject(deferred.promise, self._W);
- }
- return;
- }
- var ret = tryCallOne(cb, self._W);
- if (ret === IS_ERROR) {
- reject(deferred.promise, LAST_ERROR);
- } else {
- resolve(deferred.promise, ret);
- }
- });
- }
- function resolve(self, newValue) {
- // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
- if (newValue === self) {
- return reject(
- self,
- new TypeError('A promise cannot be resolved with itself.')
- );
- }
- if (
- newValue &&
- (typeof newValue === 'object' || typeof newValue === 'function')
- ) {
- var then = getThen(newValue);
- if (then === IS_ERROR) {
- return reject(self, LAST_ERROR);
- }
- if (
- then === self.then &&
- newValue instanceof Promise
- ) {
- self._V = 3;
- self._W = newValue;
- finale(self);
- return;
- } else if (typeof then === 'function') {
- doResolve(then.bind(newValue), self);
- return;
- }
- }
- self._V = 1;
- self._W = newValue;
- finale(self);
- }
- function reject(self, newValue) {
- self._V = 2;
- self._W = newValue;
- if (Promise._Z) {
- Promise._Z(self, newValue);
- }
- finale(self);
- }
- function finale(self) {
- if (self._U === 1) {
- handle(self, self._X);
- self._X = null;
- }
- if (self._U === 2) {
- for (var i = 0; i < self._X.length; i++) {
- handle(self, self._X[i]);
- }
- self._X = null;
- }
- }
- function Handler(onFulfilled, onRejected, promise){
- this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
- this.onRejected = typeof onRejected === 'function' ? onRejected : null;
- this.promise = promise;
- }
- /**
- * Take a potentially misbehaving resolver function and make sure
- * onFulfilled and onRejected are only called once.
- *
- * Makes no guarantees about asynchrony.
- */
- function doResolve(fn, promise) {
- var done = false;
- var res = tryCallTwo(fn, function (value) {
- if (done) return;
- done = true;
- resolve(promise, value);
- }, function (reason) {
- if (done) return;
- done = true;
- reject(promise, reason);
- });
- if (!done && res === IS_ERROR) {
- done = true;
- reject(promise, LAST_ERROR);
- }
- }
|