1'use strict'; 2 3 4 5function noop() {} 6 7// States: 8// 9// 0 - pending 10// 1 - fulfilled with _value 11// 2 - rejected with _value 12// 3 - adopted the state of another promise, _value 13// 14// once the state is no longer pending (0) it is immutable 15 16// All `_` prefixed properties will be reduced to `_{random number}` 17// at build time to obfuscate them and discourage their use. 18// We don't use symbols or Object.defineProperty to fully hide them 19// because the performance isn't good enough. 20 21 22// to avoid using try/catch inside critical functions, we 23// extract them to here. 24var LAST_ERROR = null; 25var IS_ERROR = {}; 26function getThen(obj) { 27 try { 28 return obj.then; 29 } catch (ex) { 30 LAST_ERROR = ex; 31 return IS_ERROR; 32 } 33} 34 35function tryCallOne(fn, a) { 36 try { 37 return fn(a); 38 } catch (ex) { 39 LAST_ERROR = ex; 40 return IS_ERROR; 41 } 42} 43function tryCallTwo(fn, a, b) { 44 try { 45 fn(a, b); 46 } catch (ex) { 47 LAST_ERROR = ex; 48 return IS_ERROR; 49 } 50} 51 52module.exports = Promise; 53 54function Promise(fn) { 55 if (typeof this !== 'object') { 56 throw new TypeError('Promises must be constructed via new'); 57 } 58 if (typeof fn !== 'function') { 59 throw new TypeError('Promise constructor\'s argument is not a function'); 60 } 61 this._40 = 0; 62 this._65 = 0; 63 this._55 = null; 64 this._72 = null; 65 if (fn === noop) return; 66 doResolve(fn, this); 67} 68Promise._37 = null; 69Promise._87 = null; 70Promise._61 = noop; 71 72Promise.prototype.then = function(onFulfilled, onRejected) { 73 if (this.constructor !== Promise) { 74 return safeThen(this, onFulfilled, onRejected); 75 } 76 var res = new Promise(noop); 77 handle(this, new Handler(onFulfilled, onRejected, res)); 78 return res; 79}; 80 81function safeThen(self, onFulfilled, onRejected) { 82 return new self.constructor(function (resolve, reject) { 83 var res = new Promise(noop); 84 res.then(resolve, reject); 85 handle(self, new Handler(onFulfilled, onRejected, res)); 86 }); 87} 88function handle(self, deferred) { 89 while (self._65 === 3) { 90 self = self._55; 91 } 92 if (Promise._37) { 93 Promise._37(self); 94 } 95 if (self._65 === 0) { 96 if (self._40 === 0) { 97 self._40 = 1; 98 self._72 = deferred; 99 return; 100 } 101 if (self._40 === 1) { 102 self._40 = 2; 103 self._72 = [self._72, deferred]; 104 return; 105 } 106 self._72.push(deferred); 107 return; 108 } 109 handleResolved(self, deferred); 110} 111 112function handleResolved(self, deferred) { 113 setImmediate(function() { 114 var cb = self._65 === 1 ? deferred.onFulfilled : deferred.onRejected; 115 if (cb === null) { 116 if (self._65 === 1) { 117 resolve(deferred.promise, self._55); 118 } else { 119 reject(deferred.promise, self._55); 120 } 121 return; 122 } 123 var ret = tryCallOne(cb, self._55); 124 if (ret === IS_ERROR) { 125 reject(deferred.promise, LAST_ERROR); 126 } else { 127 resolve(deferred.promise, ret); 128 } 129 }); 130} 131function resolve(self, newValue) { 132 // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure 133 if (newValue === self) { 134 return reject( 135 self, 136 new TypeError('A promise cannot be resolved with itself.') 137 ); 138 } 139 if ( 140 newValue && 141 (typeof newValue === 'object' || typeof newValue === 'function') 142 ) { 143 var then = getThen(newValue); 144 if (then === IS_ERROR) { 145 return reject(self, LAST_ERROR); 146 } 147 if ( 148 then === self.then && 149 newValue instanceof Promise 150 ) { 151 self._65 = 3; 152 self._55 = newValue; 153 finale(self); 154 return; 155 } else if (typeof then === 'function') { 156 doResolve(then.bind(newValue), self); 157 return; 158 } 159 } 160 self._65 = 1; 161 self._55 = newValue; 162 finale(self); 163} 164 165function reject(self, newValue) { 166 self._65 = 2; 167 self._55 = newValue; 168 if (Promise._87) { 169 Promise._87(self, newValue); 170 } 171 finale(self); 172} 173function finale(self) { 174 if (self._40 === 1) { 175 handle(self, self._72); 176 self._72 = null; 177 } 178 if (self._40 === 2) { 179 for (var i = 0; i < self._72.length; i++) { 180 handle(self, self._72[i]); 181 } 182 self._72 = null; 183 } 184} 185 186function Handler(onFulfilled, onRejected, promise){ 187 this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; 188 this.onRejected = typeof onRejected === 'function' ? onRejected : null; 189 this.promise = promise; 190} 191 192/** 193 * Take a potentially misbehaving resolver function and make sure 194 * onFulfilled and onRejected are only called once. 195 * 196 * Makes no guarantees about asynchrony. 197 */ 198function doResolve(fn, promise) { 199 var done = false; 200 var res = tryCallTwo(fn, function (value) { 201 if (done) return; 202 done = true; 203 resolve(promise, value); 204 }, function (reason) { 205 if (done) return; 206 done = true; 207 reject(promise, reason); 208 }); 209 if (!done && res === IS_ERROR) { 210 done = true; 211 reject(promise, LAST_ERROR); 212 } 213} 214