src/cm/media/js/lib/yui/yui3-3.15.0/build/promise/promise-debug.js
changeset 602 e16a97fb364a
equal deleted inserted replaced
601:d334a616c023 602:e16a97fb364a
       
     1 YUI.add('promise', function (Y, NAME) {
       
     2 
       
     3 /**
       
     4 Wraps the execution of asynchronous operations, providing a promise object that
       
     5 can be used to subscribe to the various ways the operation may terminate.
       
     6 
       
     7 When the operation completes successfully, call the Resolver's `resolve()`
       
     8 method, passing any relevant response data for subscribers.  If the operation
       
     9 encounters an error or is unsuccessful in some way, call `reject()`, again
       
    10 passing any relevant data for subscribers.
       
    11 
       
    12 The Resolver object should be shared only with the code resposible for
       
    13 resolving or rejecting it. Public access for the Resolver is through its
       
    14 _promise_, which is returned from the Resolver's `promise` property. While both
       
    15 Resolver and promise allow subscriptions to the Resolver's state changes, the
       
    16 promise may be exposed to non-controlling code. It is the preferable interface
       
    17 for adding subscriptions.
       
    18 
       
    19 Subscribe to state changes in the Resolver with the promise's
       
    20 `then(callback, errback)` method.  `then()` wraps the passed callbacks in a
       
    21 new Resolver and returns the corresponding promise, allowing chaining of
       
    22 asynchronous or synchronous operations. E.g.
       
    23 `promise.then(someAsyncFunc).then(anotherAsyncFunc)`
       
    24 
       
    25 @module promise
       
    26 @since 3.9.0
       
    27 **/
       
    28 
       
    29 var Lang  = Y.Lang,
       
    30     slice = [].slice;
       
    31 
       
    32 /**
       
    33 A promise represents a value that may not yet be available. Promises allow
       
    34 you to chain asynchronous operations, write synchronous looking code and
       
    35 handle errors throughout the process.
       
    36 
       
    37 This constructor takes a function as a parameter where you can insert the logic
       
    38 that fulfills or rejects this promise. The fulfillment value and the rejection
       
    39 reason can be any JavaScript value. It's encouraged that rejection reasons be
       
    40 error objects
       
    41 
       
    42 <pre><code>
       
    43 var fulfilled = new Y.Promise(function (resolve) {
       
    44     resolve('I am a fulfilled promise');
       
    45 });
       
    46 
       
    47 var rejected = new Y.Promise(function (resolve, reject) {
       
    48     reject(new Error('I am a rejected promise'));
       
    49 });
       
    50 </code></pre>
       
    51 
       
    52 @class Promise
       
    53 @constructor
       
    54 @param {Function} fn A function where to insert the logic that resolves this
       
    55         promise. Receives `resolve` and `reject` functions as parameters.
       
    56         This function is called synchronously.
       
    57 **/
       
    58 function Promise(fn) {
       
    59     if (!(this instanceof Promise)) {
       
    60         return new Promise(fn);
       
    61     }
       
    62 
       
    63     var resolver = new Promise.Resolver(this);
       
    64 
       
    65     /**
       
    66     A reference to the resolver object that handles this promise
       
    67 
       
    68     @property _resolver
       
    69     @type Object
       
    70     @private
       
    71     */
       
    72     this._resolver = resolver;
       
    73 
       
    74     fn.call(this, function (value) {
       
    75         resolver.resolve(value);
       
    76     }, function (reason) {
       
    77         resolver.reject(reason);
       
    78     });
       
    79 }
       
    80 
       
    81 Y.mix(Promise.prototype, {
       
    82     /**
       
    83     Schedule execution of a callback to either or both of "fulfill" and
       
    84     "reject" resolutions for this promise. The callbacks are wrapped in a new
       
    85     promise and that promise is returned.  This allows operation chaining ala
       
    86     `functionA().then(functionB).then(functionC)` where `functionA` returns
       
    87     a promise, and `functionB` and `functionC` _may_ return promises.
       
    88 
       
    89     Asynchronicity of the callbacks is guaranteed.
       
    90 
       
    91     @method then
       
    92     @param {Function} [callback] function to execute if the promise
       
    93                 resolves successfully
       
    94     @param {Function} [errback] function to execute if the promise
       
    95                 resolves unsuccessfully
       
    96     @return {Promise} A promise wrapping the resolution of either "resolve" or
       
    97                 "reject" callback
       
    98     **/
       
    99     then: function (callback, errback) {
       
   100         var Constructor = this.constructor,
       
   101             resolver = this._resolver;
       
   102 
       
   103         // using this.constructor allows for customized promises to be
       
   104         // returned instead of plain ones
       
   105         return new Constructor(function (resolve, reject) {
       
   106             resolver._addCallbacks(
       
   107                 // Check if callbacks are functions. If not, default to
       
   108                 // `resolve` and `reject` respectively.
       
   109                 // The wrapping of the callbacks is done here and not in
       
   110                 // `_addCallbacks` because it is a feature specific to  `then`.
       
   111                 // If `done` is added to promises it would call `_addCallbacks`
       
   112                 // without defaulting to anything and without wrapping
       
   113                 typeof callback === 'function' ?
       
   114                     Promise._wrap(resolve, reject, callback) : resolve,
       
   115                 typeof errback === 'function' ?
       
   116                     Promise._wrap(resolve, reject, errback) : reject
       
   117             );
       
   118         });
       
   119     },
       
   120 
       
   121     /*
       
   122     A shorthand for `promise.then(undefined, callback)`.
       
   123 
       
   124     Returns a new promise and the error callback gets the same treatment as in
       
   125     `then`: errors get caught and turned into rejections, and the return value
       
   126     of the callback becomes the fulfilled value of the returned promise.
       
   127 
       
   128     @method catch
       
   129     @param [Function] errback Callback to be called in case this promise is
       
   130                         rejected
       
   131     @return {Promise} A new promise modified by the behavior of the error
       
   132                         callback
       
   133     */
       
   134     'catch': function (errback) {
       
   135         return this.then(undefined, errback);
       
   136     },
       
   137 
       
   138     /**
       
   139     Returns the current status of the operation. Possible results are
       
   140     "pending", "fulfilled", and "rejected".
       
   141 
       
   142     @method getStatus
       
   143     @return {String}
       
   144     @deprecated
       
   145     **/
       
   146     getStatus: function () {
       
   147         Y.log('promise.getStatus() will be removed in the future', 'warn', NAME);
       
   148         return this._resolver.getStatus();
       
   149     }
       
   150 });
       
   151 
       
   152 /**
       
   153 Wraps the callback in another function to catch exceptions and turn them into
       
   154 rejections.
       
   155 
       
   156 @method _wrap
       
   157 @param {Function} resolve Resolving function of the resolver that
       
   158                     handles this promise
       
   159 @param {Function} reject Rejection function of the resolver that
       
   160                     handles this promise
       
   161 @param {Function} fn Callback to wrap
       
   162 @return {Function}
       
   163 @private
       
   164 **/
       
   165 Promise._wrap = function (resolve, reject, fn) {
       
   166     // callbacks and errbacks only get one argument
       
   167     return function (valueOrReason) {
       
   168         var result;
       
   169 
       
   170         // Promises model exception handling through callbacks
       
   171         // making both synchronous and asynchronous errors behave
       
   172         // the same way
       
   173         try {
       
   174             // Use the argument coming in to the callback/errback from the
       
   175             // resolution of the parent promise.
       
   176             // The function must be called as a normal function, with no
       
   177             // special value for |this|, as per Promises A+
       
   178             result = fn(valueOrReason);
       
   179         } catch (e) {
       
   180             reject(e);
       
   181             return;
       
   182         }
       
   183 
       
   184         resolve(result);
       
   185     };
       
   186 };
       
   187 
       
   188 /**
       
   189 Checks if an object or value is a promise. This is cross-implementation
       
   190 compatible, so promises returned from other libraries or native components
       
   191 that are compatible with the Promises A+ spec should be recognized by this
       
   192 method.
       
   193 
       
   194 @method isPromise
       
   195 @param {Any} obj The object to test
       
   196 @return {Boolean} Whether the object is a promise or not
       
   197 @static
       
   198 **/
       
   199 Promise.isPromise = function (obj) {
       
   200     var then;
       
   201     // We test promises by structure to be able to identify other
       
   202     // implementations' promises. This is important for cross compatibility and
       
   203     // In particular Y.when which should recognize any kind of promise
       
   204     // Use try...catch when retrieving obj.then. Return false if it throws
       
   205     // See Promises/A+ 1.1
       
   206     try {
       
   207         then = obj.then;
       
   208     } catch (_) {}
       
   209     return typeof then === 'function';
       
   210 };
       
   211 
       
   212 /*
       
   213 Ensures that a certain value is a promise. If it is not a promise, it wraps it
       
   214 in one.
       
   215 
       
   216 This method can be copied or inherited in subclasses. In that case it will
       
   217 check that the value passed to it is an instance of the correct class.
       
   218 This means that `PromiseSubclass.resolve()` will always return instances of
       
   219 `PromiseSubclass`.
       
   220 
       
   221 @method resolve
       
   222 @param {Any} Any object that may or may not be a promise
       
   223 @return {Promise}
       
   224 @static
       
   225 */
       
   226 Promise.resolve = function (value) {
       
   227     return Promise.isPromise(value) && value.constructor === this ? value :
       
   228         /*jshint newcap: false */
       
   229         new this(function (resolve) {
       
   230         /*jshint newcap: true */
       
   231             resolve(value);
       
   232         });
       
   233 };
       
   234 
       
   235 /*
       
   236 A shorthand for creating a rejected promise.
       
   237 
       
   238 @method reject
       
   239 @param {Any} reason Reason for the rejection of this promise. Usually an Error
       
   240     Object
       
   241 @return {Promise} A rejected promise
       
   242 @static
       
   243 */
       
   244 Promise.reject = function (reason) {
       
   245     /*jshint newcap: false */
       
   246     return new this(function (resolve, reject) {
       
   247     /*jshint newcap: true */
       
   248         reject(reason);
       
   249     });
       
   250 };
       
   251 
       
   252 /*
       
   253 Returns a promise that is resolved or rejected when all values are resolved or
       
   254 any is rejected. This is useful for waiting for the resolution of multiple
       
   255 promises, such as reading multiple files in Node.js or making multiple XHR
       
   256 requests in the browser.
       
   257 
       
   258 @method all
       
   259 @param {Any[]} values An array of any kind of values, promises or not. If a value is not
       
   260 @return [Promise] A promise for an array of all the fulfillment values
       
   261 @static
       
   262 */
       
   263 Promise.all = function (values) {
       
   264     var Promise = this;
       
   265     return new Promise(function (resolve, reject) {
       
   266         if (!Lang.isArray(values)) {
       
   267             reject(new TypeError('Promise.all expects an array of values or promises'));
       
   268             return;
       
   269         }
       
   270 
       
   271         var remaining = values.length,
       
   272             i         = 0,
       
   273             length    = values.length,
       
   274             results   = [];
       
   275 
       
   276         function oneDone(index) {
       
   277             return function (value) {
       
   278                 results[index] = value;
       
   279 
       
   280                 remaining--;
       
   281 
       
   282                 if (!remaining) {
       
   283                     resolve(results);
       
   284                 }
       
   285             };
       
   286         }
       
   287 
       
   288         if (length < 1) {
       
   289             return resolve(results);
       
   290         }
       
   291 
       
   292         for (; i < length; i++) {
       
   293             Promise.resolve(values[i]).then(oneDone(i), reject);
       
   294         }
       
   295     });
       
   296 };
       
   297 
       
   298 /*
       
   299 Returns a promise that is resolved or rejected when any of values is either
       
   300 resolved or rejected. Can be used for providing early feedback in the UI
       
   301 while other operations are still pending.
       
   302 
       
   303 @method race
       
   304 @param {Any[]} values An array of values or promises
       
   305 @return {Promise}
       
   306 @static
       
   307 */
       
   308 Promise.race = function (values) {
       
   309     var Promise = this;
       
   310     return new Promise(function (resolve, reject) {
       
   311         if (!Lang.isArray(values)) {
       
   312             reject(new TypeError('Promise.race expects an array of values or promises'));
       
   313             return;
       
   314         }
       
   315         
       
   316         // just go through the list and resolve and reject at the first change
       
   317         // This abuses the fact that calling resolve/reject multiple times
       
   318         // doesn't change the state of the returned promise
       
   319         for (var i = 0, count = values.length; i < count; i++) {
       
   320             Promise.resolve(values[i]).then(resolve, reject);
       
   321         }
       
   322     });
       
   323 };
       
   324 
       
   325 Y.Promise = Promise;
       
   326 /**
       
   327 Represents an asynchronous operation. Provides a
       
   328 standard API for subscribing to the moment that the operation completes either
       
   329 successfully (`fulfill()`) or unsuccessfully (`reject()`).
       
   330 
       
   331 @class Promise.Resolver
       
   332 @constructor
       
   333 @param {Promise} promise The promise instance this resolver will be handling
       
   334 **/
       
   335 function Resolver(promise) {
       
   336     /**
       
   337     List of success callbacks
       
   338 
       
   339     @property _callbacks
       
   340     @type Array
       
   341     @private
       
   342     **/
       
   343     this._callbacks = [];
       
   344 
       
   345     /**
       
   346     List of failure callbacks
       
   347 
       
   348     @property _errbacks
       
   349     @type Array
       
   350     @private
       
   351     **/
       
   352     this._errbacks = [];
       
   353 
       
   354     /**
       
   355     The promise for this Resolver.
       
   356 
       
   357     @property promise
       
   358     @type Promise
       
   359     @deprecated
       
   360     **/
       
   361     this.promise = promise;
       
   362 
       
   363     /**
       
   364     The status of the operation. This property may take only one of the following
       
   365     values: 'pending', 'fulfilled' or 'rejected'.
       
   366 
       
   367     @property _status
       
   368     @type String
       
   369     @default 'pending'
       
   370     @private
       
   371     **/
       
   372     this._status = 'pending';
       
   373 
       
   374     /**
       
   375     This value that this promise represents.
       
   376 
       
   377     @property _result
       
   378     @type Any
       
   379     @private
       
   380     **/
       
   381     this._result = null;
       
   382 }
       
   383 
       
   384 Y.mix(Resolver.prototype, {
       
   385     /**
       
   386     Resolves the promise, signaling successful completion of the
       
   387     represented operation. All "onFulfilled" subscriptions are executed and passed
       
   388     the value provided to this method. After calling `fulfill()`, `reject()` and
       
   389     `notify()` are disabled.
       
   390 
       
   391     @method fulfill
       
   392     @param {Any} value Value to pass along to the "onFulfilled" subscribers
       
   393     **/
       
   394     fulfill: function (value) {
       
   395         if (this._status === 'pending') {
       
   396             this._result = value;
       
   397             this._status = 'fulfilled';
       
   398         }
       
   399 
       
   400         if (this._status === 'fulfilled') {
       
   401             this._notify(this._callbacks, this._result);
       
   402 
       
   403             // Reset the callback list so that future calls to fulfill()
       
   404             // won't call the same callbacks again. Promises keep a list
       
   405             // of callbacks, they're not the same as events. In practice,
       
   406             // calls to fulfill() after the first one should not be made by
       
   407             // the user but by then()
       
   408             this._callbacks = [];
       
   409 
       
   410             // Once a promise gets fulfilled it can't be rejected, so
       
   411             // there is no point in keeping the list. Remove it to help
       
   412             // garbage collection
       
   413             this._errbacks = null;
       
   414         }
       
   415     },
       
   416 
       
   417     /**
       
   418     Resolves the promise, signaling *un*successful completion of the
       
   419     represented operation. All "onRejected" subscriptions are executed with
       
   420     the value provided to this method. After calling `reject()`, `resolve()`
       
   421     and `notify()` are disabled.
       
   422 
       
   423     @method reject
       
   424     @param {Any} value Value to pass along to the "reject" subscribers
       
   425     **/
       
   426     reject: function (reason) {
       
   427         if (this._status === 'pending') {
       
   428             this._result = reason;
       
   429             this._status = 'rejected';
       
   430         }
       
   431 
       
   432         if (this._status === 'rejected') {
       
   433             if (!this._errbacks.length) { Y.log('This promise was rejected but no error handlers were registered to it', 'info', NAME); }
       
   434             this._notify(this._errbacks, this._result);
       
   435 
       
   436             // See fulfill()
       
   437             this._callbacks = null;
       
   438             this._errbacks = [];
       
   439         }
       
   440     },
       
   441 
       
   442     /*
       
   443     Given a certain value A passed as a parameter, this method resolves the
       
   444     promise to the value A.
       
   445 
       
   446     If A is a promise, `resolve` will cause the resolver to adopt the state of A
       
   447     and once A is resolved, it will resolve the resolver's promise as well.
       
   448     This behavior "flattens" A by calling `then` recursively and essentially
       
   449     disallows promises-for-promises.
       
   450 
       
   451     This is the default algorithm used when using the function passed as the
       
   452     first argument to the promise initialization function. This means that
       
   453     the following code returns a promise for the value 'hello world':
       
   454 
       
   455         var promise1 = new Y.Promise(function (resolve) {
       
   456             resolve('hello world');
       
   457         });
       
   458         var promise2 = new Y.Promise(function (resolve) {
       
   459             resolve(promise1);
       
   460         });
       
   461         promise2.then(function (value) {
       
   462             assert(value === 'hello world'); // true
       
   463         });
       
   464 
       
   465     @method resolve
       
   466     @param [Any] value A regular JS value or a promise
       
   467     */
       
   468     resolve: function (value) {
       
   469         var self = this;
       
   470 
       
   471         if (Promise.isPromise(value)) {
       
   472             value.then(function (value) {
       
   473                 self.resolve(value);
       
   474             }, function (reason) {
       
   475                 self.reject(reason);
       
   476             });
       
   477         } else {
       
   478             this.fulfill(value);
       
   479         }
       
   480     },
       
   481 
       
   482     /**
       
   483     Schedule execution of a callback to either or both of "resolve" and
       
   484     "reject" resolutions for the Resolver.  The callbacks
       
   485     are wrapped in a new Resolver and that Resolver's corresponding promise
       
   486     is returned.  This allows operation chaining ala
       
   487     `functionA().then(functionB).then(functionC)` where `functionA` returns
       
   488     a promise, and `functionB` and `functionC` _may_ return promises.
       
   489 
       
   490     @method then
       
   491     @param {Function} [callback] function to execute if the Resolver
       
   492                 resolves successfully
       
   493     @param {Function} [errback] function to execute if the Resolver
       
   494                 resolves unsuccessfully
       
   495     @return {Promise} The promise of a new Resolver wrapping the resolution
       
   496                 of either "resolve" or "reject" callback
       
   497     @deprecated
       
   498     **/
       
   499     then: function (callback, errback) {
       
   500         return this.promise.then(callback, errback);
       
   501     },
       
   502 
       
   503     /**
       
   504     Schedule execution of a callback to either or both of "resolve" and
       
   505     "reject" resolutions of this resolver. If the resolver is not pending,
       
   506     the correct callback gets called automatically.
       
   507 
       
   508     @method _addCallbacks
       
   509     @param {Function} [callback] function to execute if the Resolver
       
   510                 resolves successfully
       
   511     @param {Function} [errback] function to execute if the Resolver
       
   512                 resolves unsuccessfully
       
   513     @private
       
   514     **/
       
   515     _addCallbacks: function (callback, errback) {
       
   516         var callbackList = this._callbacks,
       
   517             errbackList  = this._errbacks,
       
   518             status       = this._status,
       
   519             result       = this._result;
       
   520 
       
   521         if (callbackList && typeof callback === 'function') {
       
   522             callbackList.push(callback);
       
   523         }
       
   524         if (errbackList && typeof errback === 'function') {
       
   525             errbackList.push(errback);
       
   526         }
       
   527 
       
   528         // If a promise is already fulfilled or rejected, notify the newly added
       
   529         // callbacks by calling fulfill() or reject()
       
   530         if (status === 'fulfilled') {
       
   531             this.fulfill(result);
       
   532         } else if (status === 'rejected') {
       
   533             this.reject(result);
       
   534         }
       
   535     },
       
   536 
       
   537     /**
       
   538     Returns the current status of the Resolver as a string "pending",
       
   539     "fulfilled", or "rejected".
       
   540 
       
   541     @method getStatus
       
   542     @return {String}
       
   543     @deprecated
       
   544     **/
       
   545     getStatus: function () {
       
   546         Y.log('resolver.getStatus() will be removed in the future', 'warn', NAME);
       
   547         return this._status;
       
   548     },
       
   549 
       
   550     /**
       
   551     Executes an array of callbacks from a specified context, passing a set of
       
   552     arguments.
       
   553 
       
   554     @method _notify
       
   555     @param {Function[]} subs The array of subscriber callbacks
       
   556     @param {Any} result Value to pass the callbacks
       
   557     @protected
       
   558     **/
       
   559     _notify: function (subs, result) {
       
   560         // Since callback lists are reset synchronously, the subs list never
       
   561         // changes after _notify() receives it. Avoid calling Y.soon() for
       
   562         // an empty list
       
   563         if (subs.length) {
       
   564             // Calling all callbacks after Y.soon to guarantee
       
   565             // asynchronicity. Because setTimeout can cause unnecessary
       
   566             // delays that *can* become noticeable in some situations
       
   567             // (especially in Node.js)
       
   568             Y.soon(function () {
       
   569                 var i, len;
       
   570 
       
   571                 for (i = 0, len = subs.length; i < len; ++i) {
       
   572                     subs[i](result);
       
   573                 }
       
   574             });
       
   575         }
       
   576     }
       
   577 
       
   578 }, true);
       
   579 
       
   580 Y.Promise.Resolver = Resolver;
       
   581 /**
       
   582 Abstraction API allowing you to interact with promises or raw values as if they
       
   583 were promises. If a non-promise object is passed in, a new Resolver is created
       
   584 and scheduled to resolve asynchronously with the provided value.
       
   585 
       
   586 In either case, a promise is returned.  If either _callback_ or _errback_ are
       
   587 provided, the promise returned is the one returned from calling
       
   588 `promise.then(callback, errback)` on the provided or created promise.  If neither
       
   589 are provided, the original promise is returned.
       
   590 
       
   591 @for YUI
       
   592 @method when
       
   593 @param {Any} promise Promise object or value to wrap in a resolved promise
       
   594 @param {Function} [callback] callback to execute if the promise is resolved
       
   595 @param {Function} [errback] callback to execute if the promise is rejected
       
   596 @return {Promise}
       
   597 **/
       
   598 Y.when = function (promise, callback, errback) {
       
   599     promise = Promise.resolve(promise);
       
   600 
       
   601     return (callback || errback) ? promise.then(callback, errback) : promise;
       
   602 };
       
   603 /**
       
   604 Returns a new promise that will be resolved when all operations have completed.
       
   605 Takes both any numer of values as arguments. If an argument is a not a promise,
       
   606 it will be wrapped in a new promise, same as in `Y.when()`.
       
   607 
       
   608 @for YUI
       
   609 @method batch
       
   610 @param {Any} operation* Any number of Y.Promise objects or regular JS values
       
   611 @return {Promise} Promise to be fulfilled when all provided promises are
       
   612                     resolved
       
   613 **/
       
   614 Y.batch = function () {
       
   615     return Promise.all(slice.call(arguments));
       
   616 };
       
   617 
       
   618 
       
   619 }, '@VERSION@', {"requires": ["timers"]});