[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
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"]});
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |