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
						
					
					
				| '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);
 | |
|       }
 | |
|     });
 | |
|   }
 | |
| }
 |