123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- 'use strict';
- var asap = require('asap');
- 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._h = 0;
- this._i = 0;
- this._j = null;
- this._k = null;
- if (fn === noop) return;
- doResolve(fn, this);
- }
- Promise._l = null;
- Promise._m = null;
- Promise._n = 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._i === 3) {
- self = self._j;
- }
- if (Promise._l) {
- Promise._l(self);
- }
- if (self._i === 0) {
- if (self._h === 0) {
- self._h = 1;
- self._k = deferred;
- return;
- }
- if (self._h === 1) {
- self._h = 2;
- self._k = [self._k, deferred];
- return;
- }
- self._k.push(deferred);
- return;
- }
- handleResolved(self, deferred);
- }
- function handleResolved(self, deferred) {
- asap(function() {
- var cb = self._i === 1 ? deferred.onFulfilled : deferred.onRejected;
- if (cb === null) {
- if (self._i === 1) {
- resolve(deferred.promise, self._j);
- } else {
- reject(deferred.promise, self._j);
- }
- return;
- }
- var ret = tryCallOne(cb, self._j);
- 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._i = 3;
- self._j = newValue;
- finale(self);
- return;
- } else if (typeof then === 'function') {
- doResolve(then.bind(newValue), self);
- return;
- }
- }
- self._i = 1;
- self._j = newValue;
- finale(self);
- }
- function reject(self, newValue) {
- self._i = 2;
- self._j = newValue;
- if (Promise._m) {
- Promise._m(self, newValue);
- }
- finale(self);
- }
- function finale(self) {
- if (self._h === 1) {
- handle(self, self._k);
- self._k = null;
- }
- if (self._h === 2) {
- for (var i = 0; i < self._k.length; i++) {
- handle(self, self._k[i]);
- }
- self._k = 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);
- }
- }
|