[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/3.17.2/promise/ -> promise.js (source)

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


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1