#

API Docs for: 3.10.3
Show: Inherited Protected Private Deprecated

# File: promise/js/resolver.js

    /**
    Represents an asynchronous operation. Provides a
    standard API for subscribing to the moment that the operation completes either
    successfully (`fulfill()`) or unsuccessfully (`reject()`).

    @class Promise.Resolver
    @constructor
    @param {Promise} promise The promise instance this resolver will be handling
    **/
    function Resolver(promise) {
        /**
        List of success callbacks

        @property _callbacks
        @type Array
        @private
        **/
        this._callbacks = [];

        /**
        List of failure callbacks

        @property _errbacks
        @type Array
        @private
        **/
        this._errbacks = [];

        /**
        The promise for this Resolver.

        @property promise
        @type Promise
        **/
        this.promise = promise;

        /**
        The status of the operation. This property may take only one of the following
        values: 'pending', 'fulfilled' or 'rejected'.

        @property _status
        @type String
        @default 'pending'
        @private
        **/
        this._status = 'pending';
    }

    Y.mix(Resolver.prototype, {
        /**
        Resolves the promise, signaling successful completion of the
        represented operation. All "onFulfilled" subscriptions are executed and passed
        the value provided to this method. After calling `fulfill()`, `reject()` and
        `notify()` are disabled.

        @method fulfill
        @param {Any} value Value to pass along to the "onFulfilled" subscribers
        **/
        fulfill: function (value) {
            if (this._status === 'pending') {
                this._result = value;
            }

            if (this._status !== 'rejected') {
                this._notify(this._callbacks, this._result);

                // Reset the callback list so that future calls to fulfill()
                // won't call the same callbacks again. Promises keep a list
                // of callbacks, they're not the same as events. In practice,
                // calls to fulfill() after the first one should not be made by
                // the user but by then()
                this._callbacks = [];

                // Once a promise gets fulfilled it can't be rejected, so
                // there is no point in keeping the list. Remove it to help
                // garbage collection
                this._errbacks = null;

                this._status = 'fulfilled';
            }
        },

        /**
        Resolves the promise, signaling *un*successful completion of the
        represented operation. All "onRejected" subscriptions are executed with
        the value provided to this method. After calling `reject()`, `resolve()`
        and `notify()` are disabled.

        @method reject
        @param {Any} value Value to pass along to the "reject" subscribers
        **/
        reject: function (reason) {
            if (this._status === 'pending') {
                this._result = reason;
            }

            if (this._status !== 'fulfilled') {
                this._notify(this._errbacks, this._result);

                // See fulfill()
                this._callbacks = null;
                this._errbacks = [];

                this._status = 'rejected';
            }
        },

        /**
        Schedule execution of a callback to either or both of "resolve" and
        "reject" resolutions for the Resolver.  The callbacks
        are wrapped in a new Resolver and that Resolver's corresponding promise
        is returned.  This allows operation chaining ala
        `functionA().then(functionB).then(functionC)` where `functionA` returns
        a promise, and `functionB` and `functionC` _may_ return promises.

        @method then
        @param {Function} [callback] function to execute if the Resolver
                    resolves successfully
        @param {Function} [errback] function to execute if the Resolver
                    resolves unsuccessfully
        @return {Promise} The promise of a new Resolver wrapping the resolution
                    of either "resolve" or "reject" callback
        **/
        then: function (callback, errback) {
            // When the current promise is fulfilled or rejected, either the
            // callback or errback will be executed via the function pushed onto
            // this._callbacks or this._errbacks.  However, to allow then()
            // chaining, the execution of either function needs to be represented
            // by a Resolver (the same Resolver can represent both flow paths), and
            // its promise returned.
            var promise = this.promise,
                thenFulfill, thenReject,

                // using promise constructor allows for customized promises to be
                // returned instead of plain ones
                then = new promise.constructor(function (fulfill, reject) {
                    thenFulfill = fulfill;
                    thenReject = reject;
                }),

                callbackList = this._callbacks || [],
                errbackList  = this._errbacks  || [];

            // Because the callback and errback are represented by a Resolver, it
            // must be fulfilled or rejected to propagate through the then() chain.
            // The same logic applies to resolve() and reject() for fulfillment.
            callbackList.push(typeof callback === 'function' ?
                this._wrap(thenFulfill, thenReject, callback) : thenFulfill);
            errbackList.push(typeof errback === 'function' ?
                this._wrap(thenFulfill, thenReject, errback) : thenReject);

            // If a promise is already fulfilled or rejected, notify the newly added
            // callbacks by calling fulfill() or reject()
            if (this._status === 'fulfilled') {
                this.fulfill(this._result);
            } else if (this._status === 'rejected') {
                this.reject(this._result);
            }

            return then;
        },

        /**
        Wraps the callback in Y.soon to guarantee its asynchronous execution. It
        also catches exceptions to turn them into rejections and links promises
        returned from the `then` callback.

        @method _wrap
        @param {Function} thenFulfill Fulfillment function of the resolver that
                            handles this promise
        @param {Function} thenReject Rejection function of the resolver that
                            handles this promise
        @param {Function} fn Callback to wrap
        @return {Function}
        @private
        **/
        _wrap: function (thenFulfill, thenReject, fn) {
            var promise = this.promise;

            return function () {
                // The args coming in to the callback/errback from the
                // resolution of the parent promise.
                var args = arguments;

                // Wrapping all callbacks in Y.soon to guarantee
                // asynchronicity. Because setTimeout can cause unnecessary
                // delays that *can* become noticeable in some situations
                // (especially in Node.js)
                Y.soon(function () {
                    // Call the callback/errback with promise as `this` to
                    // preserve the contract that access to the deferred is
                    // only for code that may resolve/reject it.
                    // Another option would be call the function from the
                    // global context, but it seemed less useful.
                    var result;

                    // Promises model exception handling through callbacks
                    // making both synchronous and asynchronous errors behave
                    // the same way
                    try {
                        result = fn.apply(promise, args);
                    } catch (e) {
                        return thenReject(e);
                    }

                    if (Promise.isPromise(result)) {
                        // Returning a promise from a callback makes the current
                        // promise sync up with the returned promise
                        result.then(thenFulfill, thenReject);
                    } else {
                        // Non-promise return values always trigger resolve()
                        // because callback is affirmative, and errback is
                        // recovery.  To continue on the rejection path, errbacks
                        // must return rejected promises or throw.
                        thenFulfill(result);
                    }
                });
            };
        },

        /**
        Returns the current status of the Resolver as a string "pending",
        "fulfilled", or "rejected".

        @method getStatus
        @return {String}
        **/
        getStatus: function () {
            return this._status;
        },

        /**
        Executes an array of callbacks from a specified context, passing a set of
        arguments.

        @method _notify
        @param {Function[]} subs The array of subscriber callbacks
        @param {Any} result Value to pass the callbacks
        @protected
        **/
        _notify: function (subs, result) {
            var i, len;

            for (i = 0, len = subs.length; i < len; ++i) {
                subs[i](result);
            }
        }

    }, true);

    Y.Promise.Resolver = Resolver;