/* Ext.promise.Deferred adapted from: [DeftJS](https://github.com/deftjs/deftjs5) Copyright (c) 2012-2013 [DeftJS Framework Contributors](http://deftjs.org) Open source under the [MIT License](http://en.wikipedia.org/wiki/MIT_License). */ /** * Deferreds are the mechanism used to create new Promises. A Deferred has a single * associated Promise that can be safely returned to external consumers to ensure they do * not interfere with the resolution or rejection of the deferred operation. * * A Deferred is typically used within the body of a function that performs an asynchronous * operation. When that operation succeeds, the Deferred should be resolved; if that * operation fails, the Deferred should be rejected. * * Each Deferred has an associated Promise. A Promise delegates `then` calls to its * Deferred's `then` method. In this way, access to Deferred operations are divided between * producer (Deferred) and consumer (Promise) roles. * * When a Deferred's `resolve` method is called, it fulfills with the optionally specified * value. If `resolve` is called with a then-able (i.e.a Function or Object with a `then` * function, such as another Promise) it assimilates the then-able's result; the Deferred * provides its own `resolve` and `reject` methods as the onFulfilled or onRejected * arguments in a call to that then-able's `then` function. If an error is thrown while * calling the then-able's `then` function (prior to any call back to the specified * `resolve` or `reject` methods), the Deferred rejects with that error. If a Deferred's * `resolve` method is called with its own Promise, it rejects with a TypeError. * * When a Deferred's `reject` method is called, it rejects with the optionally specified * reason. * * Each time a Deferred's `then` method is called, it captures a pair of optional * onFulfilled and onRejected callbacks and returns a Promise of the Deferred's future * value as transformed by those callbacks. * * @private * @since 6.0.0 */Ext.define('Ext.promise.Deferred', { requires: [ 'Ext.promise.Consequence' ], /** * @property {Ext.promise.Promise} promise Promise of the future value of this Deferred. */ promise: null, /** * @property {Ext.promise.Consequence[]} consequences Pending Consequences chained to this Deferred. * * @private */ consequences: [], /** * @property {Boolean} completed Indicates whether this Deferred has been completed. * * @private */ completed: false, /** * @property {String} completeAction The completion action (i.e. 'fulfill' or 'reject'). * * @private */ completionAction: null, /** * @property {Mixed} completionValue The completion value (i.e. resolution value or rejection error). * * @private */ completionValue: null, constructor: function() { var me = this; me.promise = new Ext.promise.Promise(me); me.consequences = []; me.completed = false; me.completionAction = null; me.completionValue = null; }, /** * 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 * 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] Callback to execute to transform a fulfillment value. * @param {Function} [onRejected] Callback to execute to transform a rejection reason. * @param {Function} [onProgress] Callback to execute to transform a progress value. * * @return Promise that is fulfilled with the callback return value or rejected with * any error thrown by the callback. */ then: function(onFulfilled, onRejected, onProgress) { var me = this, consequence = new Ext.promise.Consequence(onFulfilled, onRejected, onProgress); if (me.completed) { consequence.trigger(me.completionAction, me.completionValue); } else { me.consequences.push(consequence); } return consequence.promise; }, /** * Resolve this Deferred with the (optional) specified value. * * If called with a then-able (i.e.a Function or Object with a `then` * function, such as another Promise) it assimilates the then-able's * result; the Deferred provides its own `resolve` and `reject` methods * as the onFulfilled or onRejected arguments in a call to that * then-able's `then` function. If an error is thrown while calling * the then-able's `then` function (prior to any call back to the * specified `resolve` or `reject` methods), the Deferred rejects with * that error. If a Deferred's `resolve` method is called with its own * Promise, it rejects with a TypeError. * * Once a Deferred has been fulfilled or rejected, it is considered to be complete * and subsequent calls to `resolve` or `reject` are ignored. * * @param {Mixed} value Value to resolve as either a fulfillment value or rejection * reason. */ resolve: function(value) { var me = this, isHandled, thenFn; if (me.completed) { return; } try { if (value === me.promise) { throw new TypeError('A Promise cannot be resolved with itself.'); } if ((Ext.isObject(value) || Ext.isFunction(value)) && Ext.isFunction(thenFn = value.then)) { isHandled = false; try { thenFn.call(value, function(value) { if (!isHandled) { isHandled = true; me.resolve(value); } }, function(error) { if (!isHandled) { isHandled = true; me.reject(error); } }); } catch (e) { if (!isHandled) { me.reject(e); } } } else { me.complete('fulfill', value); } } catch (e) { me.reject(e); } }, /** * Reject this Deferred with the specified reason. * * Once a Deferred has been rejected, it is considered to be complete * and subsequent calls to `resolve` or `reject` are ignored. * * @param {Error} reason Rejection reason. */ reject: function(reason) { if (this.completed) { return; } this.complete('reject', reason); }, /** * Updates progress for this Deferred, if it is still pending, triggering it to * execute the `onProgress` callback and propagate the resulting transformed progress * value to Deferreds that originate from this Deferred. * * @param {Mixed} progress The progress value. */ update: function(progress) { var consequences = this.consequences, consequence, i, len; if (this.completed) { return; } for (i = 0, len = consequences.length; i < len; i++) { consequence = consequences[i]; consequence.update(progress); } }, /** * Complete this Deferred with the specified action and value. * * @param {String} action Completion action (i.e. 'fufill' or 'reject'). * @param {Mixed} value Fulfillment value or rejection reason. * * @private */ complete: function(action, value) { var me = this, consequences = me.consequences, consequence, i, len; me.completionAction = action; me.completionValue = value; me.completed = true; for (i = 0, len = consequences.length; i < len; i++) { consequence = consequences[i]; consequence.trigger(me.completionAction, me.completionValue); } me.consequences = null; }});