[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

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


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