You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					299 lines
				
				6.8 KiB
			
		
		
			
		
	
	
					299 lines
				
				6.8 KiB
			| 
											2 years ago
										 | 'use strict'; | ||
|  | var immediate = require('immediate'); | ||
|  | 
 | ||
|  | /* istanbul ignore next */ | ||
|  | function INTERNAL() {} | ||
|  | 
 | ||
|  | var handlers = {}; | ||
|  | 
 | ||
|  | var REJECTED = ['REJECTED']; | ||
|  | var FULFILLED = ['FULFILLED']; | ||
|  | var PENDING = ['PENDING']; | ||
|  | /* istanbul ignore else */ | ||
|  | if (!process.browser) { | ||
|  |   // in which we actually take advantage of JS scoping
 | ||
|  |   var UNHANDLED = ['UNHANDLED']; | ||
|  | } | ||
|  | 
 | ||
|  | module.exports = Promise; | ||
|  | 
 | ||
|  | function Promise(resolver) { | ||
|  |   if (typeof resolver !== 'function') { | ||
|  |     throw new TypeError('resolver must be a function'); | ||
|  |   } | ||
|  |   this.state = PENDING; | ||
|  |   this.queue = []; | ||
|  |   this.outcome = void 0; | ||
|  |   /* istanbul ignore else */ | ||
|  |   if (!process.browser) { | ||
|  |     this.handled = UNHANDLED; | ||
|  |   } | ||
|  |   if (resolver !== INTERNAL) { | ||
|  |     safelyResolveThenable(this, resolver); | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | Promise.prototype.finally = function (callback) { | ||
|  |   if (typeof callback !== 'function') { | ||
|  |     return this; | ||
|  |   } | ||
|  |   var p = this.constructor; | ||
|  |   return this.then(resolve, reject); | ||
|  | 
 | ||
|  |   function resolve(value) { | ||
|  |     function yes () { | ||
|  |       return value; | ||
|  |     } | ||
|  |     return p.resolve(callback()).then(yes); | ||
|  |   } | ||
|  |   function reject(reason) { | ||
|  |     function no () { | ||
|  |       throw reason; | ||
|  |     } | ||
|  |     return p.resolve(callback()).then(no); | ||
|  |   } | ||
|  | }; | ||
|  | Promise.prototype.catch = function (onRejected) { | ||
|  |   return this.then(null, onRejected); | ||
|  | }; | ||
|  | Promise.prototype.then = function (onFulfilled, onRejected) { | ||
|  |   if (typeof onFulfilled !== 'function' && this.state === FULFILLED || | ||
|  |     typeof onRejected !== 'function' && this.state === REJECTED) { | ||
|  |     return this; | ||
|  |   } | ||
|  |   var promise = new this.constructor(INTERNAL); | ||
|  |   /* istanbul ignore else */ | ||
|  |   if (!process.browser) { | ||
|  |     if (this.handled === UNHANDLED) { | ||
|  |       this.handled = null; | ||
|  |     } | ||
|  |   } | ||
|  |   if (this.state !== PENDING) { | ||
|  |     var resolver = this.state === FULFILLED ? onFulfilled : onRejected; | ||
|  |     unwrap(promise, resolver, this.outcome); | ||
|  |   } else { | ||
|  |     this.queue.push(new QueueItem(promise, onFulfilled, onRejected)); | ||
|  |   } | ||
|  | 
 | ||
|  |   return promise; | ||
|  | }; | ||
|  | function QueueItem(promise, onFulfilled, onRejected) { | ||
|  |   this.promise = promise; | ||
|  |   if (typeof onFulfilled === 'function') { | ||
|  |     this.onFulfilled = onFulfilled; | ||
|  |     this.callFulfilled = this.otherCallFulfilled; | ||
|  |   } | ||
|  |   if (typeof onRejected === 'function') { | ||
|  |     this.onRejected = onRejected; | ||
|  |     this.callRejected = this.otherCallRejected; | ||
|  |   } | ||
|  | } | ||
|  | QueueItem.prototype.callFulfilled = function (value) { | ||
|  |   handlers.resolve(this.promise, value); | ||
|  | }; | ||
|  | QueueItem.prototype.otherCallFulfilled = function (value) { | ||
|  |   unwrap(this.promise, this.onFulfilled, value); | ||
|  | }; | ||
|  | QueueItem.prototype.callRejected = function (value) { | ||
|  |   handlers.reject(this.promise, value); | ||
|  | }; | ||
|  | QueueItem.prototype.otherCallRejected = function (value) { | ||
|  |   unwrap(this.promise, this.onRejected, value); | ||
|  | }; | ||
|  | 
 | ||
|  | function unwrap(promise, func, value) { | ||
|  |   immediate(function () { | ||
|  |     var returnValue; | ||
|  |     try { | ||
|  |       returnValue = func(value); | ||
|  |     } catch (e) { | ||
|  |       return handlers.reject(promise, e); | ||
|  |     } | ||
|  |     if (returnValue === promise) { | ||
|  |       handlers.reject(promise, new TypeError('Cannot resolve promise with itself')); | ||
|  |     } else { | ||
|  |       handlers.resolve(promise, returnValue); | ||
|  |     } | ||
|  |   }); | ||
|  | } | ||
|  | 
 | ||
|  | handlers.resolve = function (self, value) { | ||
|  |   var result = tryCatch(getThen, value); | ||
|  |   if (result.status === 'error') { | ||
|  |     return handlers.reject(self, result.value); | ||
|  |   } | ||
|  |   var thenable = result.value; | ||
|  | 
 | ||
|  |   if (thenable) { | ||
|  |     safelyResolveThenable(self, thenable); | ||
|  |   } else { | ||
|  |     self.state = FULFILLED; | ||
|  |     self.outcome = value; | ||
|  |     var i = -1; | ||
|  |     var len = self.queue.length; | ||
|  |     while (++i < len) { | ||
|  |       self.queue[i].callFulfilled(value); | ||
|  |     } | ||
|  |   } | ||
|  |   return self; | ||
|  | }; | ||
|  | handlers.reject = function (self, error) { | ||
|  |   self.state = REJECTED; | ||
|  |   self.outcome = error; | ||
|  |   /* istanbul ignore else */ | ||
|  |   if (!process.browser) { | ||
|  |     if (self.handled === UNHANDLED) { | ||
|  |       immediate(function () { | ||
|  |         if (self.handled === UNHANDLED) { | ||
|  |           process.emit('unhandledRejection', error, self); | ||
|  |         } | ||
|  |       }); | ||
|  |     } | ||
|  |   } | ||
|  |   var i = -1; | ||
|  |   var len = self.queue.length; | ||
|  |   while (++i < len) { | ||
|  |     self.queue[i].callRejected(error); | ||
|  |   } | ||
|  |   return self; | ||
|  | }; | ||
|  | 
 | ||
|  | function getThen(obj) { | ||
|  |   // Make sure we only access the accessor once as required by the spec
 | ||
|  |   var then = obj && obj.then; | ||
|  |   if (obj && (typeof obj === 'object' || typeof obj === 'function') && typeof then === 'function') { | ||
|  |     return function appyThen() { | ||
|  |       then.apply(obj, arguments); | ||
|  |     }; | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | function safelyResolveThenable(self, thenable) { | ||
|  |   // Either fulfill, reject or reject with error
 | ||
|  |   var called = false; | ||
|  |   function onError(value) { | ||
|  |     if (called) { | ||
|  |       return; | ||
|  |     } | ||
|  |     called = true; | ||
|  |     handlers.reject(self, value); | ||
|  |   } | ||
|  | 
 | ||
|  |   function onSuccess(value) { | ||
|  |     if (called) { | ||
|  |       return; | ||
|  |     } | ||
|  |     called = true; | ||
|  |     handlers.resolve(self, value); | ||
|  |   } | ||
|  | 
 | ||
|  |   function tryToUnwrap() { | ||
|  |     thenable(onSuccess, onError); | ||
|  |   } | ||
|  | 
 | ||
|  |   var result = tryCatch(tryToUnwrap); | ||
|  |   if (result.status === 'error') { | ||
|  |     onError(result.value); | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | function tryCatch(func, value) { | ||
|  |   var out = {}; | ||
|  |   try { | ||
|  |     out.value = func(value); | ||
|  |     out.status = 'success'; | ||
|  |   } catch (e) { | ||
|  |     out.status = 'error'; | ||
|  |     out.value = e; | ||
|  |   } | ||
|  |   return out; | ||
|  | } | ||
|  | 
 | ||
|  | Promise.resolve = resolve; | ||
|  | function resolve(value) { | ||
|  |   if (value instanceof this) { | ||
|  |     return value; | ||
|  |   } | ||
|  |   return handlers.resolve(new this(INTERNAL), value); | ||
|  | } | ||
|  | 
 | ||
|  | Promise.reject = reject; | ||
|  | function reject(reason) { | ||
|  |   var promise = new this(INTERNAL); | ||
|  |   return handlers.reject(promise, reason); | ||
|  | } | ||
|  | 
 | ||
|  | Promise.all = all; | ||
|  | function all(iterable) { | ||
|  |   var self = this; | ||
|  |   if (Object.prototype.toString.call(iterable) !== '[object Array]') { | ||
|  |     return this.reject(new TypeError('must be an array')); | ||
|  |   } | ||
|  | 
 | ||
|  |   var len = iterable.length; | ||
|  |   var called = false; | ||
|  |   if (!len) { | ||
|  |     return this.resolve([]); | ||
|  |   } | ||
|  | 
 | ||
|  |   var values = new Array(len); | ||
|  |   var resolved = 0; | ||
|  |   var i = -1; | ||
|  |   var promise = new this(INTERNAL); | ||
|  | 
 | ||
|  |   while (++i < len) { | ||
|  |     allResolver(iterable[i], i); | ||
|  |   } | ||
|  |   return promise; | ||
|  |   function allResolver(value, i) { | ||
|  |     self.resolve(value).then(resolveFromAll, function (error) { | ||
|  |       if (!called) { | ||
|  |         called = true; | ||
|  |         handlers.reject(promise, error); | ||
|  |       } | ||
|  |     }); | ||
|  |     function resolveFromAll(outValue) { | ||
|  |       values[i] = outValue; | ||
|  |       if (++resolved === len && !called) { | ||
|  |         called = true; | ||
|  |         handlers.resolve(promise, values); | ||
|  |       } | ||
|  |     } | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | Promise.race = race; | ||
|  | function race(iterable) { | ||
|  |   var self = this; | ||
|  |   if (Object.prototype.toString.call(iterable) !== '[object Array]') { | ||
|  |     return this.reject(new TypeError('must be an array')); | ||
|  |   } | ||
|  | 
 | ||
|  |   var len = iterable.length; | ||
|  |   var called = false; | ||
|  |   if (!len) { | ||
|  |     return this.resolve([]); | ||
|  |   } | ||
|  | 
 | ||
|  |   var i = -1; | ||
|  |   var promise = new this(INTERNAL); | ||
|  | 
 | ||
|  |   while (++i < len) { | ||
|  |     resolver(iterable[i]); | ||
|  |   } | ||
|  |   return promise; | ||
|  |   function resolver(value) { | ||
|  |     self.resolve(value).then(function (response) { | ||
|  |       if (!called) { | ||
|  |         called = true; | ||
|  |         handlers.resolve(promise, response); | ||
|  |       } | ||
|  |     }, function (error) { | ||
|  |       if (!called) { | ||
|  |         called = true; | ||
|  |         handlers.reject(promise, error); | ||
|  |       } | ||
|  |     }); | ||
|  |   } | ||
|  | } |