AutorÃa | Ultima modificación | Ver Log |
YUI.add('promise', function (Y, NAME) {/**Wraps the execution of asynchronous operations, providing a promise object thatcan be used to subscribe to the various ways the operation may terminate.When the operation completes successfully, call the Resolver's `resolve()`method, passing any relevant response data for subscribers. If the operationencounters an error or is unsuccessful in some way, call `reject()`, againpassing any relevant data for subscribers.The Resolver object should be shared only with the code resposible forresolving or rejecting it. Public access for the Resolver is through its_promise_, which is returned from the Resolver's `promise` property. While bothResolver and promise allow subscriptions to the Resolver's state changes, thepromise may be exposed to non-controlling code. It is the preferable interfacefor adding subscriptions.Subscribe to state changes in the Resolver with the promise's`then(callback, errback)` method. `then()` wraps the passed callbacks in anew Resolver and returns the corresponding promise, allowing chaining ofasynchronous or synchronous operations. E.g.`promise.then(someAsyncFunc).then(anotherAsyncFunc)`@module promise@since 3.9.0**/var Lang = Y.Lang,slice = [].slice;/**A promise represents a value that may not yet be available. Promises allowyou to chain asynchronous operations, write synchronous looking code andhandle errors throughout the process.This constructor takes a function as a parameter where you can insert the logicthat fulfills or rejects this promise. The fulfillment value and the rejectionreason can be any JavaScript value. It's encouraged that rejection reasons beerror objects<pre><code>var fulfilled = new Y.Promise(function (resolve) {resolve('I am a fulfilled promise');});var rejected = new Y.Promise(function (resolve, reject) {reject(new Error('I am a rejected promise'));});</code></pre>@class Promise@constructor@param {Function} fn A function where to insert the logic that resolves thispromise. Receives `resolve` and `reject` functions as parameters.This function is called synchronously.**/function Promise(fn) {if (!(this instanceof Promise)) {Y.log('Promises should always be created with new Promise(). This will throw an error in the future', 'warn', NAME);return new Promise(fn);}var resolver = new Promise.Resolver(this);/**A reference to the resolver object that handles this promise@property _resolver@type Object@private*/this._resolver = resolver;try {fn.call(this, function (value) {resolver.resolve(value);}, function (reason) {resolver.reject(reason);});} catch (e) {resolver.reject(e);}}Y.mix(Promise.prototype, {/**Schedule execution of a callback to either or both of "fulfill" and"reject" resolutions for this promise. The callbacks are wrapped in a newpromise and that promise is returned. This allows operation chaining ala`functionA().then(functionB).then(functionC)` where `functionA` returnsa promise, and `functionB` and `functionC` _may_ return promises.Asynchronicity of the callbacks is guaranteed.@method then@param {Function} [callback] function to execute if the promiseresolves successfully@param {Function} [errback] function to execute if the promiseresolves unsuccessfully@return {Promise} A promise wrapping the resolution of either "resolve" or"reject" callback**/then: function (callback, errback) {var Constructor = this.constructor,resolver = this._resolver;// using this.constructor allows for customized promises to be// returned instead of plain onesreturn new Constructor(function (resolve, reject) {resolver._addCallbacks(// Check if callbacks are functions. If not, default to// `resolve` and `reject` respectively.// The wrapping of the callbacks is done here and not in// `_addCallbacks` because it is a feature specific to `then`.// If `done` is added to promises it would call `_addCallbacks`// without defaulting to anything and without wrappingtypeof callback === 'function' ?Promise._wrap(resolve, reject, callback) : resolve,typeof errback === 'function' ?Promise._wrap(resolve, reject, errback) : reject);});},/**A shorthand for `promise.then(undefined, callback)`.Returns a new promise and the error callback gets the same treatment as in`then`: errors get caught and turned into rejections, and the return valueof the callback becomes the fulfilled value of the returned promise.@method catch@param [Function] errback Callback to be called in case this promise isrejected@return {Promise} A new promise modified by the behavior of the errorcallback**/'catch': function (errback) {return this.then(undefined, errback);},/**Returns the current status of the operation. Possible results are"pending", "fulfilled", and "rejected".@method getStatus@return {String}@deprecated**/getStatus: function () {Y.log('promise.getStatus() will be removed in the future', 'warn', NAME);return this._resolver.getStatus();}});/**Wraps the callback in another function to catch exceptions and turn them intorejections.@method _wrap@param {Function} resolve Resolving function of the resolver thathandles this promise@param {Function} reject Rejection function of the resolver thathandles this promise@param {Function} fn Callback to wrap@return {Function}@private**/Promise._wrap = function (resolve, reject, fn) {// callbacks and errbacks only get one argumentreturn function (valueOrReason) {var result;// Promises model exception handling through callbacks// making both synchronous and asynchronous errors behave// the same waytry {// Use the argument coming in to the callback/errback from the// resolution of the parent promise.// The function must be called as a normal function, with no// special value for |this|, as per Promises A+result = fn(valueOrReason);} catch (e) {reject(e);return;}resolve(result);};};/**Checks if an object or value is a promise. This is cross-implementationcompatible, so promises returned from other libraries or native componentsthat are compatible with the Promises A+ spec should be recognized by thismethod.@method isPromise@param {Any} obj The object to test@return {Boolean} Whether the object is a promise or not@static**/Promise.isPromise = function (obj) {var then;// We test promises by structure to be able to identify other// implementations' promises. This is important for cross compatibility and// In particular Y.when which should recognize any kind of promise// Use try...catch when retrieving obj.then. Return false if it throws// See Promises/A+ 1.1try {then = obj.then;} catch (_) {}return typeof then === 'function';};/**Ensures that a certain value is a promise. If it is not a promise, it wraps itin one.This method can be copied or inherited in subclasses. In that case it willcheck that the value passed to it is an instance of the correct class.This means that `PromiseSubclass.resolve()` will always return instances of`PromiseSubclass`.@method resolve@param {Any} Any object that may or may not be a promise@return {Promise}@static**/Promise.resolve = function (value) {return Promise.isPromise(value) && value.constructor === this ? value :/*jshint newcap: false */new this(function (resolve) {/*jshint newcap: true */resolve(value);});};/**A shorthand for creating a rejected promise.@method reject@param {Any} reason Reason for the rejection of this promise. Usually an ErrorObject@return {Promise} A rejected promise@static**/Promise.reject = function (reason) {/*jshint newcap: false */return new this(function (resolve, reject) {/*jshint newcap: true */reject(reason);});};/**Returns a promise that is resolved or rejected when all values are resolved orany is rejected. This is useful for waiting for the resolution of multiplepromises, such as reading multiple files in Node.js or making multiple XHRrequests in the browser.@method all@param {Any[]} values An array of any kind of values, promises or not. If a value is not@return [Promise] A promise for an array of all the fulfillment values@static**/Promise.all = function (values) {var Promise = this;return new Promise(function (resolve, reject) {if (!Lang.isArray(values)) {reject(new TypeError('Promise.all expects an array of values or promises'));return;}var remaining = values.length,i = 0,length = values.length,results = [];function oneDone(index) {return function (value) {results[index] = value;remaining--;if (!remaining) {resolve(results);}};}if (length < 1) {return resolve(results);}for (; i < length; i++) {Promise.resolve(values[i]).then(oneDone(i), reject);}});};/**Returns a promise that is resolved or rejected when any of values is eitherresolved or rejected. Can be used for providing early feedback in the UIwhile other operations are still pending.@method race@param {Any[]} values An array of values or promises@return {Promise}@static**/Promise.race = function (values) {var Promise = this;return new Promise(function (resolve, reject) {if (!Lang.isArray(values)) {reject(new TypeError('Promise.race expects an array of values or promises'));return;}// just go through the list and resolve and reject at the first change// This abuses the fact that calling resolve/reject multiple times// doesn't change the state of the returned promisefor (var i = 0, count = values.length; i < count; i++) {Promise.resolve(values[i]).then(resolve, reject);}});};Y.Promise = Promise;/**Represents an asynchronous operation. Provides astandard API for subscribing to the moment that the operation completes eithersuccessfully (`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@deprecated**/this.promise = promise;/**The status of the operation. This property may take only one of the followingvalues: 'pending', 'fulfilled' or 'rejected'.@property _status@type String@default 'pending'@private**/this._status = 'pending';/**This value that this promise represents.@property _result@type Any@private**/this._result = null;}Y.mix(Resolver.prototype, {/**Resolves the promise, signaling successful completion of therepresented operation. All "onFulfilled" subscriptions are executed and passedthe 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;this._status = 'fulfilled';}if (this._status === 'fulfilled') {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 collectionthis._errbacks = null;}},/**Resolves the promise, signaling *un*successful completion of therepresented operation. All "onRejected" subscriptions are executed withthe 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;this._status = 'rejected';}if (this._status === 'rejected') {if (!this._errbacks.length) { Y.log('This promise was rejected but no error handlers were registered to it', 'info', NAME); }this._notify(this._errbacks, this._result);// See fulfill()this._callbacks = null;this._errbacks = [];}},/*Given a certain value A passed as a parameter, this method resolves thepromise to the value A.If A is a promise, `resolve` will cause the resolver to adopt the state of Aand once A is resolved, it will resolve the resolver's promise as well.This behavior "flattens" A by calling `then` recursively and essentiallydisallows promises-for-promises.This is the default algorithm used when using the function passed as thefirst argument to the promise initialization function. This means thatthe following code returns a promise for the value 'hello world':var promise1 = new Y.Promise(function (resolve) {resolve('hello world');});var promise2 = new Y.Promise(function (resolve) {resolve(promise1);});promise2.then(function (value) {assert(value === 'hello world'); // true});@method resolve@param [Any] value A regular JS value or a promise*/resolve: function (value) {var self = this;if (Promise.isPromise(value)) {value.then(function (value) {self.resolve(value);}, function (reason) {self.reject(reason);});} else {this.fulfill(value);}},/**Schedule execution of a callback to either or both of "resolve" and"reject" resolutions for the Resolver. The callbacksare wrapped in a new Resolver and that Resolver's corresponding promiseis returned. This allows operation chaining ala`functionA().then(functionB).then(functionC)` where `functionA` returnsa promise, and `functionB` and `functionC` _may_ return promises.@method then@param {Function} [callback] function to execute if the Resolverresolves successfully@param {Function} [errback] function to execute if the Resolverresolves unsuccessfully@return {Promise} The promise of a new Resolver wrapping the resolutionof either "resolve" or "reject" callback@deprecated**/then: function (callback, errback) {return this.promise.then(callback, errback);},/**Schedule execution of a callback to either or both of "resolve" and"reject" resolutions of this resolver. If the resolver is not pending,the correct callback gets called automatically.@method _addCallbacks@param {Function} [callback] function to execute if the Resolverresolves successfully@param {Function} [errback] function to execute if the Resolverresolves unsuccessfully@private**/_addCallbacks: function (callback, errback) {var callbackList = this._callbacks,errbackList = this._errbacks,status = this._status,result = this._result;if (callbackList && typeof callback === 'function') {callbackList.push(callback);}if (errbackList && typeof errback === 'function') {errbackList.push(errback);}// If a promise is already fulfilled or rejected, notify the newly added// callbacks by calling fulfill() or reject()if (status === 'fulfilled') {this.fulfill(result);} else if (status === 'rejected') {this.reject(result);}},/**Returns the current status of the Resolver as a string "pending","fulfilled", or "rejected".@method getStatus@return {String}@deprecated**/getStatus: function () {Y.log('resolver.getStatus() will be removed in the future', 'warn', NAME);return this._status;},/**Executes an array of callbacks from a specified context, passing a set ofarguments.@method _notify@param {Function[]} subs The array of subscriber callbacks@param {Any} result Value to pass the callbacks@protected**/_notify: function (subs, result) {// Since callback lists are reset synchronously, the subs list never// changes after _notify() receives it. Avoid calling Y.soon() for// an empty listif (subs.length) {// Calling all callbacks after 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 () {var i, len;for (i = 0, len = subs.length; i < len; ++i) {subs[i](result);}});}}}, true);Y.Promise.Resolver = Resolver;/**Abstraction API allowing you to interact with promises or raw values as if theywere promises. If a non-promise object is passed in, a new Resolver is createdand scheduled to resolve asynchronously with the provided value.In either case, a promise is returned. If either _callback_ or _errback_ areprovided, the promise returned is the one returned from calling`promise.then(callback, errback)` on the provided or created promise. If neitherare provided, the original promise is returned.@for YUI@method when@param {Any} promise Promise object or value to wrap in a resolved promise@param {Function} [callback] callback to execute if the promise is resolved@param {Function} [errback] callback to execute if the promise is rejected@return {Promise}**/Y.when = function (promise, callback, errback) {promise = Promise.resolve(promise);return (callback || errback) ? promise.then(callback, errback) : promise;};/**Returns a new promise that will be resolved when all operations have completed.Takes both any numer of values as arguments. If an argument is a not a promise,it will be wrapped in a new promise, same as in `Y.when()`.@for YUI@method batch@param {Any} operation* Any number of Y.Promise objects or regular JS values@return {Promise} Promise to be fulfilled when all provided promises areresolved**/Y.batch = function () {return Promise.all(slice.call(arguments));};}, '3.18.1', {"requires": ["timers"]});