| // Promise polyfill from https://github.com/taylorhakes/promise-polyfill |
| // License: |
| //============================================================================== |
| // Copyright (c) 2014 Taylor Hakes |
| // Copyright (c) 2014 Forbes Lindesay |
| // |
| // Permission is hereby granted, free of charge, to any person obtaining a copy |
| // of this software and associated documentation files (the "Software"), to deal |
| // in the Software without restriction, including without limitation the rights |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| // copies of the Software, and to permit persons to whom the Software is |
| // furnished to do so, subject to the following conditions: |
| // |
| // The above copyright notice and this permission notice shall be included in |
| // all copies or substantial portions of the Software. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| // THE SOFTWARE. |
| //============================================================================== |
| |
| /** @suppress{duplicate} This is already defined in from Closure's built-in |
| externs.zip//es6.js, Closure should not yell when seeing this again. */ |
| var Promise = (function() { |
| function noop() {} |
| |
| // Polyfill for Function.prototype.bind |
| function bind(fn, thisArg) { |
| return function() { |
| fn.apply(thisArg, arguments); |
| }; |
| } |
| |
| /** |
| * @constructor |
| * @param {Function} fn |
| */ |
| function Promise(fn) { |
| if (!(this instanceof Promise)) |
| throw new TypeError('Promises must be constructed via new'); |
| if (typeof fn !== 'function') throw new TypeError('not a function'); |
| /** @type {!number} */ |
| this._state = 0; |
| /** @type {!boolean} */ |
| this._handled = false; |
| /** @type {Promise|undefined} */ |
| this._value = undefined; |
| /** @type {!Array<!Function>} */ |
| this._deferreds = []; |
| |
| doResolve(fn, this); |
| } |
| |
| function handle(self, deferred) { |
| while (self._state === 3) { |
| self = self._value; |
| } |
| if (self._state === 0) { |
| self._deferreds.push(deferred); |
| return; |
| } |
| self._handled = true; |
| Promise._immediateFn(function() { |
| var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; |
| if (cb === null) { |
| (self._state === 1 ? resolve : reject)(deferred.promise, self._value); |
| return; |
| } |
| var ret; |
| try { |
| ret = cb(self._value); |
| } catch (e) { |
| reject(deferred.promise, e); |
| return; |
| } |
| resolve(deferred.promise, ret); |
| }); |
| } |
| |
| function resolve(self, newValue) { |
| try { |
| // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure |
| if (newValue === self) |
| throw new TypeError('A promise cannot be resolved with itself.'); |
| if ( |
| newValue && |
| (typeof newValue === 'object' || typeof newValue === 'function') |
| ) { |
| var then = newValue.then; |
| if (newValue instanceof Promise) { |
| self._state = 3; |
| self._value = newValue; |
| finale(self); |
| return; |
| } else if (typeof then === 'function') { |
| doResolve(bind(then, newValue), self); |
| return; |
| } |
| } |
| self._state = 1; |
| self._value = newValue; |
| finale(self); |
| } catch (e) { |
| reject(self, e); |
| } |
| } |
| |
| function reject(self, newValue) { |
| self._state = 2; |
| self._value = newValue; |
| finale(self); |
| } |
| |
| function finale(self) { |
| if (self._state === 2 && self._deferreds.length === 0) { |
| Promise._immediateFn(function() { |
| if (!self._handled) { |
| Promise._unhandledRejectionFn(self._value); |
| } |
| }); |
| } |
| |
| for (var i = 0, len = self._deferreds.length; i < len; i++) { |
| handle(self, self._deferreds[i]); |
| } |
| self._deferreds = null; |
| } |
| |
| /** |
| * @constructor |
| */ |
| 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, self) { |
| var done = false; |
| try { |
| fn( |
| function(value) { |
| if (done) return; |
| done = true; |
| resolve(self, value); |
| }, |
| function(reason) { |
| if (done) return; |
| done = true; |
| reject(self, reason); |
| } |
| ); |
| } catch (ex) { |
| if (done) return; |
| done = true; |
| reject(self, ex); |
| } |
| } |
| |
| Promise.prototype['catch'] = function(onRejected) { |
| return this.then(null, onRejected); |
| }; |
| |
| Promise.prototype.then = function(onFulfilled, onRejected) { |
| // @ts-ignore |
| var prom = new this.constructor(noop); |
| |
| handle(this, new Handler(onFulfilled, onRejected, prom)); |
| return prom; |
| }; |
| |
| Promise.all = function(arr) { |
| return new Promise(function(resolve, reject) { |
| if (!Array.isArray(arr)) { |
| return reject(new TypeError('Promise.all accepts an array')); |
| } |
| |
| var args = Array.prototype.slice.call(arr); |
| if (args.length === 0) return resolve([]); |
| var remaining = args.length; |
| |
| function res(i, val) { |
| try { |
| if (val && (typeof val === 'object' || typeof val === 'function')) { |
| var then = val.then; |
| if (typeof then === 'function') { |
| then.call( |
| val, |
| function(val) { |
| res(i, val); |
| }, |
| reject |
| ); |
| return; |
| } |
| } |
| args[i] = val; |
| if (--remaining === 0) { |
| resolve(args); |
| } |
| } catch (ex) { |
| reject(ex); |
| } |
| } |
| |
| for (var i = 0; i < args.length; i++) { |
| res(i, args[i]); |
| } |
| }); |
| }; |
| |
| Promise.resolve = function(value) { |
| if (value && typeof value === 'object' && value.constructor === Promise) { |
| return value; |
| } |
| |
| return new Promise(function(resolve) { |
| resolve(value); |
| }); |
| }; |
| |
| Promise.reject = function(value) { |
| return new Promise(function(resolve, reject) { |
| reject(value); |
| }); |
| }; |
| |
| Promise.race = function(arr) { |
| return new Promise(function(resolve, reject) { |
| if (!Array.isArray(arr)) { |
| return reject(new TypeError('Promise.race accepts an array')); |
| } |
| |
| for (var i = 0, len = arr.length; i < len; i++) { |
| Promise.resolve(arr[i]).then(resolve, reject); |
| } |
| }); |
| }; |
| |
| // Use polyfill for setImmediate for performance gains |
| Promise._immediateFn = |
| // @ts-ignore |
| (typeof setImmediate === 'function' && |
| function(fn) { |
| // @ts-ignore |
| setImmediate(fn); |
| }) || |
| function(fn) { |
| setTimeout(fn, 0); // XXX EMSCRIPTEN: just use setTimeout |
| }; |
| |
| Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) { |
| if (typeof console !== 'undefined' && console) { |
| console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console |
| } |
| }; |
| |
| return Promise; |
| })(); |
| |