src/cm/media/js/lib/yui/yui3-3.15.0/build/promise/promise.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         return this._resolver.getStatus();
       
   148     }
       
   149 });
       
   150 
       
   151 /**
       
   152 Wraps the callback in another function to catch exceptions and turn them into
       
   153 rejections.
       
   154 
       
   155 @method _wrap
       
   156 @param {Function} resolve Resolving function of the resolver that
       
   157                     handles this promise
       
   158 @param {Function} reject Rejection function of the resolver that
       
   159                     handles this promise
       
   160 @param {Function} fn Callback to wrap
       
   161 @return {Function}
       
   162 @private
       
   163 **/
       
   164 Promise._wrap = function (resolve, reject, fn) {
       
   165     // callbacks and errbacks only get one argument
       
   166     return function (valueOrReason) {
       
   167         var result;
       
   168 
       
   169         // Promises model exception handling through callbacks
       
   170         // making both synchronous and asynchronous errors behave
       
   171         // the same way
       
   172         try {
       
   173             // Use the argument coming in to the callback/errback from the
       
   174             // resolution of the parent promise.
       
   175             // The function must be called as a normal function, with no
       
   176             // special value for |this|, as per Promises A+
       
   177             result = fn(valueOrReason);
       
   178         } catch (e) {
       
   179             reject(e);
       
   180             return;
       
   181         }
       
   182 
       
   183         resolve(result);
       
   184     };
       
   185 };
       
   186 
       
   187 /**
       
   188 Checks if an object or value is a promise. This is cross-implementation
       
   189 compatible, so promises returned from other libraries or native components
       
   190 that are compatible with the Promises A+ spec should be recognized by this
       
   191 method.
       
   192 
       
   193 @method isPromise
       
   194 @param {Any} obj The object to test
       
   195 @return {Boolean} Whether the object is a promise or not
       
   196 @static
       
   197 **/
       
   198 Promise.isPromise = function (obj) {
       
   199     var then;
       
   200     // We test promises by structure to be able to identify other
       
   201     // implementations' promises. This is important for cross compatibility and
       
   202     // In particular Y.when which should recognize any kind of promise
       
   203     // Use try...catch when retrieving obj.then. Return false if it throws
       
   204     // See Promises/A+ 1.1
       
   205     try {
       
   206         then = obj.then;
       
   207     } catch (_) {}
       
   208     return typeof then === 'function';
       
   209 };
       
   210 
       
   211 /*
       
   212 Ensures that a certain value is a promise. If it is not a promise, it wraps it
       
   213 in one.
       
   214 
       
   215 This method can be copied or inherited in subclasses. In that case it will
       
   216 check that the value passed to it is an instance of the correct class.
       
   217 This means that `PromiseSubclass.resolve()` will always return instances of
       
   218 `PromiseSubclass`.
       
   219 
       
   220 @method resolve
       
   221 @param {Any} Any object that may or may not be a promise
       
   222 @return {Promise}
       
   223 @static
       
   224 */
       
   225 Promise.resolve = function (value) {
       
   226     return Promise.isPromise(value) && value.constructor === this ? value :
       
   227         /*jshint newcap: false */
       
   228         new this(function (resolve) {
       
   229         /*jshint newcap: true */
       
   230             resolve(value);
       
   231         });
       
   232 };
       
   233 
       
   234 /*
       
   235 A shorthand for creating a rejected promise.
       
   236 
       
   237 @method reject
       
   238 @param {Any} reason Reason for the rejection of this promise. Usually an Error
       
   239     Object
       
   240 @return {Promise} A rejected promise
       
   241 @static
       
   242 */
       
   243 Promise.reject = function (reason) {
       
   244     /*jshint newcap: false */
       
   245     return new this(function (resolve, reject) {
       
   246     /*jshint newcap: true */
       
   247         reject(reason);
       
   248     });
       
   249 };
       
   250 
       
   251 /*
       
   252 Returns a promise that is resolved or rejected when all values are resolved or
       
   253 any is rejected. This is useful for waiting for the resolution of multiple
       
   254 promises, such as reading multiple files in Node.js or making multiple XHR
       
   255 requests in the browser.
       
   256 
       
   257 @method all
       
   258 @param {Any[]} values An array of any kind of values, promises or not. If a value is not
       
   259 @return [Promise] A promise for an array of all the fulfillment values
       
   260 @static
       
   261 */
       
   262 Promise.all = function (values) {
       
   263     var Promise = this;
       
   264     return new Promise(function (resolve, reject) {
       
   265         if (!Lang.isArray(values)) {
       
   266             reject(new TypeError('Promise.all expects an array of values or promises'));
       
   267             return;
       
   268         }
       
   269 
       
   270         var remaining = values.length,
       
   271             i         = 0,
       
   272             length    = values.length,
       
   273             results   = [];
       
   274 
       
   275         function oneDone(index) {
       
   276             return function (value) {
       
   277                 results[index] = value;
       
   278 
       
   279                 remaining--;
       
   280 
       
   281                 if (!remaining) {
       
   282                     resolve(results);
       
   283                 }
       
   284             };
       
   285         }
       
   286 
       
   287         if (length < 1) {
       
   288             return resolve(results);
       
   289         }
       
   290 
       
   291         for (; i < length; i++) {
       
   292             Promise.resolve(values[i]).then(oneDone(i), reject);
       
   293         }
       
   294     });
       
   295 };
       
   296 
       
   297 /*
       
   298 Returns a promise that is resolved or rejected when any of values is either
       
   299 resolved or rejected. Can be used for providing early feedback in the UI
       
   300 while other operations are still pending.
       
   301 
       
   302 @method race
       
   303 @param {Any[]} values An array of values or promises
       
   304 @return {Promise}
       
   305 @static
       
   306 */
       
   307 Promise.race = function (values) {
       
   308     var Promise = this;
       
   309     return new Promise(function (resolve, reject) {
       
   310         if (!Lang.isArray(values)) {
       
   311             reject(new TypeError('Promise.race expects an array of values or promises'));
       
   312             return;
       
   313         }
       
   314         
       
   315         // just go through the list and resolve and reject at the first change
       
   316         // This abuses the fact that calling resolve/reject multiple times
       
   317         // doesn't change the state of the returned promise
       
   318         for (var i = 0, count = values.length; i < count; i++) {
       
   319             Promise.resolve(values[i]).then(resolve, reject);
       
   320         }
       
   321     });
       
   322 };
       
   323 
       
   324 Y.Promise = Promise;
       
   325 /**
       
   326 Represents an asynchronous operation. Provides a
       
   327 standard API for subscribing to the moment that the operation completes either
       
   328 successfully (`fulfill()`) or unsuccessfully (`reject()`).
       
   329 
       
   330 @class Promise.Resolver
       
   331 @constructor
       
   332 @param {Promise} promise The promise instance this resolver will be handling
       
   333 **/
       
   334 function Resolver(promise) {
       
   335     /**
       
   336     List of success callbacks
       
   337 
       
   338     @property _callbacks
       
   339     @type Array
       
   340     @private
       
   341     **/
       
   342     this._callbacks = [];
       
   343 
       
   344     /**
       
   345     List of failure callbacks
       
   346 
       
   347     @property _errbacks
       
   348     @type Array
       
   349     @private
       
   350     **/
       
   351     this._errbacks = [];
       
   352 
       
   353     /**
       
   354     The promise for this Resolver.
       
   355 
       
   356     @property promise
       
   357     @type Promise
       
   358     @deprecated
       
   359     **/
       
   360     this.promise = promise;
       
   361 
       
   362     /**
       
   363     The status of the operation. This property may take only one of the following
       
   364     values: 'pending', 'fulfilled' or 'rejected'.
       
   365 
       
   366     @property _status
       
   367     @type String
       
   368     @default 'pending'
       
   369     @private
       
   370     **/
       
   371     this._status = 'pending';
       
   372 
       
   373     /**
       
   374     This value that this promise represents.
       
   375 
       
   376     @property _result
       
   377     @type Any
       
   378     @private
       
   379     **/
       
   380     this._result = null;
       
   381 }
       
   382 
       
   383 Y.mix(Resolver.prototype, {
       
   384     /**
       
   385     Resolves the promise, signaling successful completion of the
       
   386     represented operation. All "onFulfilled" subscriptions are executed and passed
       
   387     the value provided to this method. After calling `fulfill()`, `reject()` and
       
   388     `notify()` are disabled.
       
   389 
       
   390     @method fulfill
       
   391     @param {Any} value Value to pass along to the "onFulfilled" subscribers
       
   392     **/
       
   393     fulfill: function (value) {
       
   394         if (this._status === 'pending') {
       
   395             this._result = value;
       
   396             this._status = 'fulfilled';
       
   397         }
       
   398 
       
   399         if (this._status === 'fulfilled') {
       
   400             this._notify(this._callbacks, this._result);
       
   401 
       
   402             // Reset the callback list so that future calls to fulfill()
       
   403             // won't call the same callbacks again. Promises keep a list
       
   404             // of callbacks, they're not the same as events. In practice,
       
   405             // calls to fulfill() after the first one should not be made by
       
   406             // the user but by then()
       
   407             this._callbacks = [];
       
   408 
       
   409             // Once a promise gets fulfilled it can't be rejected, so
       
   410             // there is no point in keeping the list. Remove it to help
       
   411             // garbage collection
       
   412             this._errbacks = null;
       
   413         }
       
   414     },
       
   415 
       
   416     /**
       
   417     Resolves the promise, signaling *un*successful completion of the
       
   418     represented operation. All "onRejected" subscriptions are executed with
       
   419     the value provided to this method. After calling `reject()`, `resolve()`
       
   420     and `notify()` are disabled.
       
   421 
       
   422     @method reject
       
   423     @param {Any} value Value to pass along to the "reject" subscribers
       
   424     **/
       
   425     reject: function (reason) {
       
   426         if (this._status === 'pending') {
       
   427             this._result = reason;
       
   428             this._status = 'rejected';
       
   429         }
       
   430 
       
   431         if (this._status === 'rejected') {
       
   432             this._notify(this._errbacks, this._result);
       
   433 
       
   434             // See fulfill()
       
   435             this._callbacks = null;
       
   436             this._errbacks = [];
       
   437         }
       
   438     },
       
   439 
       
   440     /*
       
   441     Given a certain value A passed as a parameter, this method resolves the
       
   442     promise to the value A.
       
   443 
       
   444     If A is a promise, `resolve` will cause the resolver to adopt the state of A
       
   445     and once A is resolved, it will resolve the resolver's promise as well.
       
   446     This behavior "flattens" A by calling `then` recursively and essentially
       
   447     disallows promises-for-promises.
       
   448 
       
   449     This is the default algorithm used when using the function passed as the
       
   450     first argument to the promise initialization function. This means that
       
   451     the following code returns a promise for the value 'hello world':
       
   452 
       
   453         var promise1 = new Y.Promise(function (resolve) {
       
   454             resolve('hello world');
       
   455         });
       
   456         var promise2 = new Y.Promise(function (resolve) {
       
   457             resolve(promise1);
       
   458         });
       
   459         promise2.then(function (value) {
       
   460             assert(value === 'hello world'); // true
       
   461         });
       
   462 
       
   463     @method resolve
       
   464     @param [Any] value A regular JS value or a promise
       
   465     */
       
   466     resolve: function (value) {
       
   467         var self = this;
       
   468 
       
   469         if (Promise.isPromise(value)) {
       
   470             value.then(function (value) {
       
   471                 self.resolve(value);
       
   472             }, function (reason) {
       
   473                 self.reject(reason);
       
   474             });
       
   475         } else {
       
   476             this.fulfill(value);
       
   477         }
       
   478     },
       
   479 
       
   480     /**
       
   481     Schedule execution of a callback to either or both of "resolve" and
       
   482     "reject" resolutions for the Resolver.  The callbacks
       
   483     are wrapped in a new Resolver and that Resolver's corresponding promise
       
   484     is returned.  This allows operation chaining ala
       
   485     `functionA().then(functionB).then(functionC)` where `functionA` returns
       
   486     a promise, and `functionB` and `functionC` _may_ return promises.
       
   487 
       
   488     @method then
       
   489     @param {Function} [callback] function to execute if the Resolver
       
   490                 resolves successfully
       
   491     @param {Function} [errback] function to execute if the Resolver
       
   492                 resolves unsuccessfully
       
   493     @return {Promise} The promise of a new Resolver wrapping the resolution
       
   494                 of either "resolve" or "reject" callback
       
   495     @deprecated
       
   496     **/
       
   497     then: function (callback, errback) {
       
   498         return this.promise.then(callback, errback);
       
   499     },
       
   500 
       
   501     /**
       
   502     Schedule execution of a callback to either or both of "resolve" and
       
   503     "reject" resolutions of this resolver. If the resolver is not pending,
       
   504     the correct callback gets called automatically.
       
   505 
       
   506     @method _addCallbacks
       
   507     @param {Function} [callback] function to execute if the Resolver
       
   508                 resolves successfully
       
   509     @param {Function} [errback] function to execute if the Resolver
       
   510                 resolves unsuccessfully
       
   511     @private
       
   512     **/
       
   513     _addCallbacks: function (callback, errback) {
       
   514         var callbackList = this._callbacks,
       
   515             errbackList  = this._errbacks,
       
   516             status       = this._status,
       
   517             result       = this._result;
       
   518 
       
   519         if (callbackList && typeof callback === 'function') {
       
   520             callbackList.push(callback);
       
   521         }
       
   522         if (errbackList && typeof errback === 'function') {
       
   523             errbackList.push(errback);
       
   524         }
       
   525 
       
   526         // If a promise is already fulfilled or rejected, notify the newly added
       
   527         // callbacks by calling fulfill() or reject()
       
   528         if (status === 'fulfilled') {
       
   529             this.fulfill(result);
       
   530         } else if (status === 'rejected') {
       
   531             this.reject(result);
       
   532         }
       
   533     },
       
   534 
       
   535     /**
       
   536     Returns the current status of the Resolver as a string "pending",
       
   537     "fulfilled", or "rejected".
       
   538 
       
   539     @method getStatus
       
   540     @return {String}
       
   541     @deprecated
       
   542     **/
       
   543     getStatus: function () {
       
   544         return this._status;
       
   545     },
       
   546 
       
   547     /**
       
   548     Executes an array of callbacks from a specified context, passing a set of
       
   549     arguments.
       
   550 
       
   551     @method _notify
       
   552     @param {Function[]} subs The array of subscriber callbacks
       
   553     @param {Any} result Value to pass the callbacks
       
   554     @protected
       
   555     **/
       
   556     _notify: function (subs, result) {
       
   557         // Since callback lists are reset synchronously, the subs list never
       
   558         // changes after _notify() receives it. Avoid calling Y.soon() for
       
   559         // an empty list
       
   560         if (subs.length) {
       
   561             // Calling all callbacks after Y.soon to guarantee
       
   562             // asynchronicity. Because setTimeout can cause unnecessary
       
   563             // delays that *can* become noticeable in some situations
       
   564             // (especially in Node.js)
       
   565             Y.soon(function () {
       
   566                 var i, len;
       
   567 
       
   568                 for (i = 0, len = subs.length; i < len; ++i) {
       
   569                     subs[i](result);
       
   570                 }
       
   571             });
       
   572         }
       
   573     }
       
   574 
       
   575 }, true);
       
   576 
       
   577 Y.Promise.Resolver = Resolver;
       
   578 /**
       
   579 Abstraction API allowing you to interact with promises or raw values as if they
       
   580 were promises. If a non-promise object is passed in, a new Resolver is created
       
   581 and scheduled to resolve asynchronously with the provided value.
       
   582 
       
   583 In either case, a promise is returned.  If either _callback_ or _errback_ are
       
   584 provided, the promise returned is the one returned from calling
       
   585 `promise.then(callback, errback)` on the provided or created promise.  If neither
       
   586 are provided, the original promise is returned.
       
   587 
       
   588 @for YUI
       
   589 @method when
       
   590 @param {Any} promise Promise object or value to wrap in a resolved promise
       
   591 @param {Function} [callback] callback to execute if the promise is resolved
       
   592 @param {Function} [errback] callback to execute if the promise is rejected
       
   593 @return {Promise}
       
   594 **/
       
   595 Y.when = function (promise, callback, errback) {
       
   596     promise = Promise.resolve(promise);
       
   597 
       
   598     return (callback || errback) ? promise.then(callback, errback) : promise;
       
   599 };
       
   600 /**
       
   601 Returns a new promise that will be resolved when all operations have completed.
       
   602 Takes both any numer of values as arguments. If an argument is a not a promise,
       
   603 it will be wrapped in a new promise, same as in `Y.when()`.
       
   604 
       
   605 @for YUI
       
   606 @method batch
       
   607 @param {Any} operation* Any number of Y.Promise objects or regular JS values
       
   608 @return {Promise} Promise to be fulfilled when all provided promises are
       
   609                     resolved
       
   610 **/
       
   611 Y.batch = function () {
       
   612     return Promise.all(slice.call(arguments));
       
   613 };
       
   614 
       
   615 
       
   616 }, '@VERSION@', {"requires": ["timers"]});