    /* Ext.promise.Deferred adapted from: [DeftJS](https://github.com/deftjs/deftjs5) Copyright (c) 2012-2014 [DeftJS Framework Contributors](http://deftjs.org) Open source under the [MIT License](http://en.wikipedia.org/wiki/MIT_License). */ /** * Promises represent a future value; i.e., a value that may not yet be available. * * Users should **not** create instances of this class directly. Instead user code should * use `new {@link Ext.Promise}()` or `new {@link Ext.Deferred}()` to create and manage * promises. If the browser supports the standard `Promise` constructor, this class will * not be used by `Ext.Promise`. This class will always be used by `Ext.Deferred` in order * to provide enhanced capabilities beyond standard promises. * * A Promise's `{@link #then then()}` method is used to specify onFulfilled and onRejected * callbacks that will be notified when the future value becomes available. Those callbacks * can subsequently transform the value that was resolved or the reason that was rejected. * Each call to `then` returns a new Promise of that transformed value; i.e., a Promise * that is resolved with the callback return value or rejected with any error thrown by * the callback. * * ## Basic Usage * *      this.companyService.loadCompanies().then( *          function (records) { *              // Do something with result. *          }, *          function (error) { *              // Do something on failure. *          }). *      always(function () { *          // Do something whether call succeeded or failed *      }); * * The above code uses the `Promise` returned from the `companyService.loadCompanies()` * method and uses `then()` to attach success and failure handlers. Finally, an `always()` * method call is chained onto the returned promise. This specifies a callback function * that will run whether the underlying call succeeded or failed. * * See `{@link Ext.Deferred}` for an example of using the returned Promise. * * [1]: http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts#april_14_2015_rev_38_final_draft * * @since 6.0.0 */Ext.define('Ext.promise.Promise', function (ExtPromise) {    var Deferred; return {    requires: [        'Ext.promise.Deferred'    ],     statics: {        /**         * @property CancellationError         * @static         * The type of `Error` propagated by the `{@link #method-cancel}` method. If         * the browser provides a native `CancellationError` then that type is used. If         * not, a basic `Error` type is used.         */        CancellationError: Ext.global.CancellationError || Error,         _ready: function () {            // Our requires are met, so we can cache Ext.promise.Deferred             Deferred = Ext.promise.Deferred;        },         /**         * Returns a new Promise that will only resolve once all the specified         * `promisesOrValues` have resolved.         *         * The resolution value will be an Array containing the resolution value of each         * of the `promisesOrValues`.         *         * The public API's to use instead of this method are `{@link Ext.Promise#all}`         * and `{@link Ext.Deferred#all}`.         *         * @param {Mixed[]/Ext.promise.Promise[]/Ext.promise.Promise} promisesOrValues An         * Array of values or Promises, or a Promise of an Array of values or Promises.         * @return {Ext.promise.Promise} A Promise of an Array of the resolved values.         *         * @static         * @private         */        all: function (promisesOrValues) {            //<debug>             if (!(Ext.isArray(promisesOrValues) || ExtPromise.is(promisesOrValues))) {                Ext.raise('Invalid parameter: expected an Array or Promise of an Array.');            }            //</debug>              return ExtPromise.when(promisesOrValues).then(function (promisesOrValues) {                var deferred = new Deferred(),                    remainingToResolve = promisesOrValues.length,                    results = new Array(remainingToResolve),                    index, promiseOrValue, resolve, i, len;                 if (!remainingToResolve) {                    deferred.resolve(results);                }                else {                    resolve = function (item, index) {                        return ExtPromise.when(item).then (function (value) {                            results[index] = value;                             if (!--remainingToResolve) {                                deferred.resolve(results);                            }                             return value;                        }, function (reason) {                            return deferred.reject(reason);                        });                    };                     for (index = i = 0, len = promisesOrValues.length; i < len; index = ++i) {                        promiseOrValue = promisesOrValues[index];                         if (index in promisesOrValues) {                            resolve(promiseOrValue, index);                        }                        else {                            remainingToResolve--;                        }                    }                }                 return deferred.promise;            });        },         /**         * Determines whether the specified value is a Promise (including third-party         * untrusted Promises or then()-ables), based on the Promises/A specification         * feature test.         *         * @param {Mixed} value A potential Promise.         * @return {Boolean} `true` if the given value is a Promise, otherwise `false`.         * @static         * @private         */        is: function (value) {            return value != null && (typeof value === 'object' || Ext.isFunction(value)) &&                Ext.isFunction(value.then);        },         /**         * Rethrows the specified Error on the next turn of the event loop.         * @static         * @private         */        rethrowError: function (error) {            Ext.asap(function () {                throw error;            });        },         /**         * Returns a new Promise that either         *         *  * Resolves immediately for the specified value, or         *  * Resolves or rejects when the specified promise (or third-party Promise or         *    then()-able) is resolved or rejected.         *         * The public API's to use instead of this method are `{@link Ext.Promise#resolve}`         * and `{@link Ext.Deferred#resolved}`.         *         * @param {Mixed} promiseOrValue A Promise (or third-party Promise or then()-able)         * or value.         * @return {Ext.Promise} A Promise of the specified Promise or value.         *         * @static         * @private         */        when: function (value) {            var deferred = new Ext.promise.Deferred();             deferred.resolve(value);             return deferred.promise;        }    },     /**     * @property {Ext.promise.Deferred} Reference to this promise's     * `{@link Ext.promise.Deferred Deferred}` instance.     *     * @readonly     * @private     */    owner: null,     /**     * NOTE: {@link Ext.promise.Deferred Deferreds} are the mechanism used to create new     * Promises.     * @param {Ext.promise.Deferred} owner The owning `Deferred` instance.     *     * @private     */    constructor: function (owner) {        this.owner = owner;    },     /**     * Attaches onFulfilled and onRejected callbacks that will be notified when the future     * value becomes available.     *     * Those callbacks can subsequently transform the value that was fulfilled or the error     * that was rejected. Each call to `then` returns a new Promise of that transformed     * value; i.e., a Promise that is fulfilled with the callback return value or rejected     * with any error thrown by the callback.     *     * @param {Function} onFulfilled Optional callback to execute to transform a     * fulfillment value.     * @param {Function} onRejected Optional callback to execute to transform a rejection     * reason.     * @param {Function} onProgress Optional callback function to be called with progress     * updates.     * @param {Object} scope Optional scope for the callback(s).     * @return {Ext.promise.Promise} Promise that is fulfilled with the callback return     * value or rejected with any error thrown by the callback.     */    then: function (onFulfilled, onRejected, onProgress, scope) {        var ref;         if (arguments.length === 1 && Ext.isObject(arguments[0])) {            ref = arguments[0];            onFulfilled = ref.success;            onRejected = ref.failure;            onProgress = ref.progress;            scope = ref.scope;        }         if (scope) {            if (onFulfilled) {                onFulfilled = Ext.Function.bind(onFulfilled, scope);            }                        if (onRejected) {                onRejected = Ext.Function.bind(onRejected, scope);            }                        if (onProgress) {                onProgress = Ext.Function.bind(onProgress, scope);            }        }         return this.owner.then(onFulfilled, onRejected, onProgress);    },     /**     * Attaches an onRejected callback that will be notified if this Promise is rejected.     *     * The callback can subsequently transform the reason that was rejected. Each call to     * `otherwise` returns a new Promise of that transformed value; i.e., a Promise that     * is resolved with the original resolved value, or resolved with the callback return     * value or rejected with any error thrown by the callback.     *     * @param {Function} onRejected Callback to execute to transform a rejection reason.     * @param {Object} scope Optional scope for the callback.     * @return {Ext.promise.Promise} Promise of the transformed future value.     */    otherwise: function (onRejected, scope) {        var ref;         if (arguments.length === 1 && Ext.isObject(arguments[0])) {            ref = arguments[0];            onRejected = ref.fn;            scope = ref.scope;        }         if (scope != null) {            onRejected = Ext.Function.bind(onRejected, scope);        }         return this.owner.then(null, onRejected);    },     /**     * Attaches an onCompleted callback that will be notified when this Promise is completed.     *     * Similar to `finally` in `try... catch... finally`.     *     * NOTE: The specified callback does not affect the resulting Promise's outcome; any     * return value is ignored and any Error is rethrown.     *     * @param {Function} onCompleted Callback to execute when the Promise is resolved or     * rejected.     * @param {Object} scope Optional scope for the callback.     * @return {Ext.promise.Promise} A new "pass-through" Promise that is resolved with     * the original value or rejected with the original reason.     */    always: function (onCompleted, scope) {        var ref;         if (arguments.length === 1 && Ext.isObject(arguments[0])) {            ref = arguments[0];            onCompleted = ref.fn;            scope = ref.scope;        }         if (scope != null) {            onCompleted = Ext.Function.bind(onCompleted, scope);        }         return this.owner.then(function (value) {            try {                onCompleted();            }            catch (e) {                ExtPromise.rethrowError(e);            }                        return value;        }, function (reason) {            try {                onCompleted();            }            catch (e) {                ExtPromise.rethrowError(e);            }                        throw reason;        });    },     /**     * Terminates a Promise chain, ensuring that unhandled rejections will be rethrown as     * Errors.     *     * One of the pitfalls of interacting with Promise-based APIs is the tendency for     * important errors to be silently swallowed unless an explicit rejection handler is     * specified.     *     * For example:     *     *      promise.then(function () {     *          // logic in your callback throws an error and it is interpreted as a     *          // rejection. throw new Error("Boom!");     *      });     *     *      // The Error was not handled by the Promise chain and is silently swallowed.     *     * This problem can be addressed by terminating the Promise chain with the done()     * method:     *     *      promise.then(function () {     *          // logic in your callback throws an error and it is interpreted as a     *          // rejection. throw new Error("Boom!");     *      }).done();     *     *     // The Error was not handled by the Promise chain and is rethrown by done() on     *     // the next tick.     *     * The `done()` method ensures that any unhandled rejections are rethrown as Errors.     */    done: function () {        this.owner.then(null, ExtPromise.rethrowError);    },     /**     * Cancels this Promise if it is still pending, triggering a rejection with a     * `{@link #CancellationError}` that will propagate to any Promises originating from     * this Promise.     *     * NOTE: Cancellation only propagates to Promises that branch from the target Promise.     * It does not traverse back up to parent branches, as this would reject nodes from     * which other Promises may have branched, causing unintended side-effects.     *     * @param {Error} reason Cancellation reason.     */    cancel: function (reason) {        if (reason == null) {            reason = null;        }                this.owner.reject(new this.self.CancellationError(reason));    },     /**     * Logs the resolution or rejection of this Promise with the specified category and     * optional identifier. Messages are logged via all registered custom logger functions.     *     * @param {String} identifier An optional identifier to incorporate into the     * resulting log entry.     *     * @return {Ext.promise.Promise} A new "pass-through" Promise that is resolved with     * the original value or rejected with the original reason.     */    log: function (identifier) {        if (identifier == null) {            identifier = '';        }                return this.owner.then(function (value) {            Ext.log("" + (identifier || 'Promise') + " resolved with value: " + value);                        return value;        }, function (reason) {            Ext.log("" + (identifier || 'Promise') + " rejected with reason: " + reason);                        throw reason;        });    }}},function (ExtPromise) {    ExtPromise._ready();});