All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.javascript.jscomp.js.es6.promise.promise.js Maven / Gradle / Ivy

Go to download

Closure Compiler is a JavaScript optimizing compiler. It parses your JavaScript, analyzes it, removes dead code and rewrites and minimizes what's left. It also checks syntax, variable references, and types, and warns about common JavaScript pitfalls. It is used in many of Google's JavaScript apps, including Gmail, Google Web Search, Google Maps, and Google Docs.

There is a newer version: v20240317
Show newest version
/*
 * Copyright 2016 The Closure Compiler Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'require base';
'require es6/util/makeiterator';
'require util/global';
'require util/polyfill';

/**
 * Should we unconditionally override a native Promise implementation with our
 * own?
 * @define {boolean}
 */
$jscomp.FORCE_POLYFILL_PROMISE = false;


$jscomp.polyfill('Promise',
    /**
     * @param {*} NativePromise
     * @return {*}
     * @suppress {reportUnknownTypes}
     */
    function(NativePromise) {
  // TODO(bradfordcsmith): Do we need to add checks for standards conformance?
  //     e.g. The version of FireFox we currently use for testing has a Promise
  //     that fails to reject attempts to fulfill it with itself, but that
  //     isn't reasonably testable here.
  if (NativePromise && !$jscomp.FORCE_POLYFILL_PROMISE) {
    return NativePromise;
  }

  /**
    * Schedules code to be executed asynchronously.
    * @constructor
    * @struct
    */
  function AsyncExecutor() {
    /**
     * Batch of functions to execute.
     *
     * Will be `null` initially and immediately after a batch finishes
     * executing.
     * @private {?Array}
     */
    this.batch_ = null;
  }

  /**
   * Schedule a function to execute asynchronously.
   *
   * -   The function will execute:
   *     -   After the current call stack has completed executing.
   *     -   After any functions previously scheduled using this object.
   * -   The return value will be ignored.
   * -   An exception thrown by the method will be caught and asynchronously
   *     rethrown when it cannot interrupt any other code. This class provides
   *     no way to catch such exceptions.
   * @param {function():?} f
   */
  AsyncExecutor.prototype.asyncExecute = function(f) {
    if (this.batch_ == null) {
      // no batch created yet, or last batch was fully executed
      this.batch_ = [];
      var self = this;
      this.asyncExecuteFunction(function() { self.executeBatch_(); });
    }
    this.batch_.push(f);
  };

  // NOTE: We want to make sure AsyncExecutor will work as expected even if
  // testing code should override setTimeout()
  /** @const {function(!Function, number)} */
  var nativeSetTimeout = $jscomp.global['setTimeout'];

  /**
   * Schedule a function to execute asynchronously as soon as possible.
   *
   * NOTE: May be overridden for testing.
   * @package
   * @param {function()} f
   */
  AsyncExecutor.prototype.asyncExecuteFunction = function(f) {
    nativeSetTimeout(f, 0);
  };

  /**
   * Execute scheduled jobs in a batch until all are executed or the batch
   * execution time limit has been reached.
   * @private
   */
  AsyncExecutor.prototype.executeBatch_ = function() {
    while (this.batch_ && this.batch_.length) {
      var /** !Array */ executingBatch = this.batch_;
      // Executions scheduled while executing this batch go into a new one to
      // avoid the batch array getting too big.
      this.batch_ = [];
      for (var i = 0; i < executingBatch.length; ++i) {
        var f = /** @type {function()} */ (executingBatch[i]);
        executingBatch[i] = null;  // free memory
        try {
          f();
        } catch (error) {
          this.asyncThrow_(error);
        }
      }
    }
    // All jobs finished executing, so force scheduling a new batch next
    // time asyncExecute() is called.
    this.batch_ = null;
  };

  /**
   * @private
   * @param {*} exception
   */
  AsyncExecutor.prototype.asyncThrow_ = function(exception) {
    this.asyncExecuteFunction(function() { throw exception; });
  };

  /**
   * @enum {number}
   */
  var PromiseState = {
    /** The Promise is waiting for resolution. */
    PENDING: 0,

    /** The Promise has been resolved with a fulfillment value. */
    FULFILLED: 1,

    /** The Promise has been resolved with a rejection reason. */
    REJECTED: 2
  };


  /**
   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
   * @param {function(
   *             function((TYPE|IThenable|Thenable|null)=),
   *             function(*=))} executor
   * @constructor
   * @extends {Promise}
   * @template TYPE
   */
  var PolyfillPromise = function(executor) {
    /** @private {PromiseState} */
    this.state_ = PromiseState.PENDING;

    /**
     * The settled result of the Promise. Immutable once set with either a
     * fulfillment value or rejection reason.
     * @private {*}
     */
    this.result_ = undefined;

    /**
     * These functions must be asynchronously executed when this promise
     * settles.
     * @private {?Array}
     */
    this.onSettledCallbacks_ = [];

    var resolveAndReject = this.createResolveAndReject_();
    try {
      executor(resolveAndReject.resolve, resolveAndReject.reject);
    } catch (e) {
      resolveAndReject.reject(e);
    }
  };


  /**
   * Create a pair of functions for resolving or rejecting this Promise.
   *
   * 

After the resolve or reject function has been called once, later calls * do nothing. * @private * @return {{ * resolve: function((TYPE|IThenable|Thenable|null)=), * reject: function(*=) * }} */ PolyfillPromise.prototype.createResolveAndReject_ = function() { var thisPromise = this; var alreadyCalled = false; /** * @param {function(this:PolyfillPromise, T=)} method * @return {function(T=)} * @template T */ function firstCallWins(method) { return function(/** T= */ x) { if (!alreadyCalled) { alreadyCalled = true; method.call(thisPromise, x); } }; } return { resolve: firstCallWins(this.resolveTo_), reject: firstCallWins(this.reject_) }; }; /** * @private * @param {*=} value */ PolyfillPromise.prototype.resolveTo_ = function(value) { if (value === this) { this.reject_(new TypeError('A Promise cannot resolve to itself')); } else if (value instanceof PolyfillPromise) { this.settleSameAsPromise_(/** @type {!PolyfillPromise} */ (value)); } else if (isObject(value)) { this.resolveToNonPromiseObj_(/** @type {!Object} */ (value)); } else { this.fulfill_(value); } }; /** * @private * @param {!Object} obj * @suppress {strictMissingProperties} obj.then */ PolyfillPromise.prototype.resolveToNonPromiseObj_ = function(obj) { var thenMethod = undefined; try { thenMethod = obj.then; } catch (error) { this.reject_(error); return; } if (typeof thenMethod == 'function') { this.settleSameAsThenable_(thenMethod, /** @type {!Thenable} */ (obj)); } else { this.fulfill_(obj); } }; /** * @param {*} value anything * @return {boolean} */ function isObject(value) { switch (typeof value) { case 'object': return value != null; case 'function': return true; default: return false; } } /** * Reject this promise for the given reason. * @private * @param {*=} reason * @throws {!Error} if this promise is already fulfilled or rejected. */ PolyfillPromise.prototype.reject_ = function(reason) { this.settle_(PromiseState.REJECTED, reason); }; /** * Fulfill this promise with the given value. * @private * @param {!TYPE} value * @throws {!Error} when this promise is already fulfilled or rejected. */ PolyfillPromise.prototype.fulfill_ = function(value) { this.settle_(PromiseState.FULFILLED, value); }; /** * Fulfill or reject this promise with the given value/reason. * @private * @param {!PromiseState} settledState (FULFILLED or REJECTED) * @param {*} valueOrReason * @throws {!Error} when this promise is already fulfilled or rejected. */ PolyfillPromise.prototype.settle_ = function(settledState, valueOrReason) { if (this.state_ != PromiseState.PENDING) { throw new Error( 'Cannot settle(' + settledState + ', ' + valueOrReason + '): Promise already settled in state' + this.state_); } this.state_ = settledState; this.result_ = valueOrReason; this.executeOnSettledCallbacks_(); }; PolyfillPromise.prototype.executeOnSettledCallbacks_ = function() { if (this.onSettledCallbacks_ != null) { for (var i = 0; i < this.onSettledCallbacks_.length; ++i) { asyncExecutor.asyncExecute(this.onSettledCallbacks_[i]); } this.onSettledCallbacks_ = null; // free memory } }; /** * All promise async execution is managed by a single executor for the * sake of efficiency. * @const {!AsyncExecutor} */ var asyncExecutor = new AsyncExecutor(); /** * Arrange to settle this promise in the same way as the given thenable. * @private * @param {!PolyfillPromise} promise */ PolyfillPromise.prototype.settleSameAsPromise_ = function(promise) { var methods = this.createResolveAndReject_(); // Calling then() would create an unnecessary extra promise. promise.callWhenSettled_(methods.resolve, methods.reject); }; /** * Arrange to settle this promise in the same way as the given thenable. * @private * @param {function( * function((TYPE|IThenable|Thenable|null)=), * function(*=)) * } thenMethod * @param {!Thenable} thenable */ PolyfillPromise.prototype.settleSameAsThenable_ = function( thenMethod, thenable) { var methods = this.createResolveAndReject_(); // Don't trust an unknown thenable implementation not to throw exceptions. try { thenMethod.call(thenable, methods.resolve, methods.reject); } catch (error) { methods.reject(error); } }; /** @override */ PolyfillPromise.prototype.then = function(onFulfilled, onRejected) { var resolveChild; var rejectChild; var childPromise = new PolyfillPromise(function(resolve, reject) { resolveChild = resolve; rejectChild = reject; }); function createCallback(paramF, defaultF) { // The spec says to ignore non-function values for onFulfilled and // onRejected if (typeof paramF == 'function') { return function(x) { try { resolveChild(paramF(x)); } catch (error) { rejectChild(error); } }; } else { return defaultF; } } this.callWhenSettled_( createCallback(onFulfilled, resolveChild), createCallback(onRejected, rejectChild)); return childPromise; }; /** @override */ PolyfillPromise.prototype.catch = function(onRejected) { return this.then(undefined, onRejected); }; PolyfillPromise.prototype.callWhenSettled_ = function( onFulfilled, onRejected) { var /** !PolyfillPromise */ thisPromise = this; function callback() { switch (thisPromise.state_) { case PromiseState.FULFILLED: onFulfilled(thisPromise.result_); break; case PromiseState.REJECTED: onRejected(thisPromise.result_); break; default: throw new Error('Unexpected state: ' + thisPromise.state_); } } if (this.onSettledCallbacks_ == null) { // we've already settled asyncExecutor.asyncExecute(callback); } else { this.onSettledCallbacks_.push(callback); } }; // called locally, so give it a name function resolvingPromise(opt_value) { if (opt_value instanceof PolyfillPromise) { return opt_value; } else { return new PolyfillPromise(function(resolve, reject) { resolve(opt_value); }); } } PolyfillPromise['resolve'] = resolvingPromise; PolyfillPromise['reject'] = function(opt_reason) { return new PolyfillPromise(function(resolve, reject) { reject(opt_reason); }); }; PolyfillPromise['race'] = function(thenablesOrValues) { return new PolyfillPromise(function(resolve, reject) { var /** !Iterator<*> */ iterator = $jscomp.makeIterator(thenablesOrValues); for (var /** !IIterableResult<*> */ iterRec = iterator.next(); !iterRec.done; iterRec = iterator.next()) { // Using resolvingPromise() allows us to treat all elements the same // way. // NOTE: resolvingPromise(promise) always returns the argument // unchanged. // Using .callWhenSettled_() instead of .then() avoids creating an // unnecessary extra promise. resolvingPromise(iterRec.value).callWhenSettled_(resolve, reject); } }); }; PolyfillPromise['all'] = function(thenablesOrValues) { var /** !Iterator<*> */ iterator = $jscomp.makeIterator(thenablesOrValues); var /** !IIterableResult<*> */ iterRec = iterator.next(); if (iterRec.done) { return resolvingPromise([]); } else { return new PolyfillPromise(function(resolveAll, rejectAll) { var resultsArray = []; var unresolvedCount = 0; function onFulfilled(i) { return function(ithResult) { resultsArray[i] = ithResult; unresolvedCount--; if (unresolvedCount == 0) { resolveAll(resultsArray); } }; } do { resultsArray.push(undefined); unresolvedCount++; // Using resolvingPromise() allows us to treat all elements the same // way. // NOTE: resolvingPromise(promise) always returns the argument // unchanged. Using .callWhenSettled_() instead of .then() avoids // creating an unnecessary extra promise. resolvingPromise(iterRec.value) .callWhenSettled_( onFulfilled(resultsArray.length - 1), rejectAll); iterRec = iterator.next(); } while (!iterRec.done); }); } }; return PolyfillPromise; }, 'es6', 'es3');





© 2015 - 2024 Weber Informatics LLC | Privacy Policy