2
YUI 3.13.0 (build 508226d)
3
Copyright 2013 Yahoo! Inc. All rights reserved.
4
Licensed under the BSD License.
5
http://yuilibrary.com/license/
8
YUI.add('promise', function (Y, NAME) {
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.
14
When the operation completes successfully, call the Resolver's `fulfill()`
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.
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.
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)`
37
A promise represents a value that may not yet be available. Promises allow
38
you to chain asynchronous operations, write synchronous looking code and
39
handle errors throughout the process.
41
This constructor takes a function as a parameter where you can insert the logic
42
that fulfills or rejects this promise. The fulfillment value and the rejection
43
reason can be any JavaScript value. It's encouraged that rejection reasons be
47
var fulfilled = new Y.Promise(function (fulfill) {
48
fulfill('I am a fulfilled promise');
51
var rejected = new Y.Promise(function (fulfill, reject) {
52
reject(new Error('I am a rejected promise'));
58
@param {Function} fn A function where to insert the logic that resolves this
59
promise. Receives `fulfill` and `reject` functions as parameters.
60
This function is called synchronously.
62
function Promise(fn) {
63
if (!(this instanceof Promise)) {
64
return new Promise(fn);
67
var resolver = new Promise.Resolver(this);
70
A reference to the resolver object that handles this promise
76
this._resolver = resolver;
78
fn.call(this, function (value) {
79
resolver.fulfill(value);
80
}, function (reason) {
81
resolver.reject(reason);
85
Y.mix(Promise.prototype, {
87
Schedule execution of a callback to either or both of "fulfill" and
88
"reject" resolutions for this promise. The callbacks are wrapped in a new
89
promise and that promise is returned. This allows operation chaining ala
90
`functionA().then(functionB).then(functionC)` where `functionA` returns
91
a promise, and `functionB` and `functionC` _may_ return promises.
93
Asynchronicity of the callbacks is guaranteed.
96
@param {Function} [callback] function to execute if the promise
98
@param {Function} [errback] function to execute if the promise
99
resolves unsuccessfully
100
@return {Promise} A promise wrapping the resolution of either "resolve" or
103
then: function (callback, errback) {
104
return this._resolver.then(callback, errback);
108
Returns the current status of the operation. Possible results are
109
"pending", "fulfilled", and "rejected".
114
getStatus: function () {
115
return this._resolver.getStatus();
120
Checks if an object or value is a promise. This is cross-implementation
121
compatible, so promises returned from other libraries or native components
122
that are compatible with the Promises A+ spec should be recognized by this
126
@param {Any} obj The object to test
127
@return {Boolean} Whether the object is a promise or not
130
Promise.isPromise = function (obj) {
131
// We test promises by form to be able to identify other implementations
132
// as promises. This is important for cross compatibility and in particular
133
// Y.when which should take any kind of promise
134
return !!obj && typeof obj.then === 'function';
139
Represents an asynchronous operation. Provides a
140
standard API for subscribing to the moment that the operation completes either
141
successfully (`fulfill()`) or unsuccessfully (`reject()`).
143
@class Promise.Resolver
145
@param {Promise} promise The promise instance this resolver will be handling
147
function Resolver(promise) {
149
List of success callbacks
155
this._callbacks = [];
158
List of failure callbacks
167
The promise for this Resolver.
172
this.promise = promise;
175
The status of the operation. This property may take only one of the following
176
values: 'pending', 'fulfilled' or 'rejected'.
183
this._status = 'pending';
186
Y.mix(Resolver.prototype, {
188
Resolves the promise, signaling successful completion of the
189
represented operation. All "onFulfilled" subscriptions are executed and passed
190
the value provided to this method. After calling `fulfill()`, `reject()` and
191
`notify()` are disabled.
194
@param {Any} value Value to pass along to the "onFulfilled" subscribers
196
fulfill: function (value) {
197
if (this._status === 'pending') {
198
this._result = value;
201
if (this._status !== 'rejected') {
202
this._notify(this._callbacks, this._result);
204
// Reset the callback list so that future calls to fulfill()
205
// won't call the same callbacks again. Promises keep a list
206
// of callbacks, they're not the same as events. In practice,
207
// calls to fulfill() after the first one should not be made by
208
// the user but by then()
209
this._callbacks = [];
211
// Once a promise gets fulfilled it can't be rejected, so
212
// there is no point in keeping the list. Remove it to help
213
// garbage collection
214
this._errbacks = null;
216
this._status = 'fulfilled';
221
Resolves the promise, signaling *un*successful completion of the
222
represented operation. All "onRejected" subscriptions are executed with
223
the value provided to this method. After calling `reject()`, `resolve()`
224
and `notify()` are disabled.
227
@param {Any} value Value to pass along to the "reject" subscribers
229
reject: function (reason) {
230
if (this._status === 'pending') {
231
this._result = reason;
234
if (this._status !== 'fulfilled') {
235
this._notify(this._errbacks, this._result);
238
this._callbacks = null;
241
this._status = 'rejected';
246
Schedule execution of a callback to either or both of "resolve" and
247
"reject" resolutions for the Resolver. The callbacks
248
are wrapped in a new Resolver and that Resolver's corresponding promise
249
is returned. This allows operation chaining ala
250
`functionA().then(functionB).then(functionC)` where `functionA` returns
251
a promise, and `functionB` and `functionC` _may_ return promises.
254
@param {Function} [callback] function to execute if the Resolver
255
resolves successfully
256
@param {Function} [errback] function to execute if the Resolver
257
resolves unsuccessfully
258
@return {Promise} The promise of a new Resolver wrapping the resolution
259
of either "resolve" or "reject" callback
261
then: function (callback, errback) {
262
// When the current promise is fulfilled or rejected, either the
263
// callback or errback will be executed via the function pushed onto
264
// this._callbacks or this._errbacks. However, to allow then()
265
// chaining, the execution of either function needs to be represented
266
// by a Resolver (the same Resolver can represent both flow paths), and
267
// its promise returned.
268
var promise = this.promise,
269
thenFulfill, thenReject,
271
// using promise constructor allows for customized promises to be
272
// returned instead of plain ones
273
then = new promise.constructor(function (fulfill, reject) {
274
thenFulfill = fulfill;
278
callbackList = this._callbacks || [],
279
errbackList = this._errbacks || [];
281
// Because the callback and errback are represented by a Resolver, it
282
// must be fulfilled or rejected to propagate through the then() chain.
283
// The same logic applies to resolve() and reject() for fulfillment.
284
callbackList.push(typeof callback === 'function' ?
285
this._wrap(thenFulfill, thenReject, callback) : thenFulfill);
286
errbackList.push(typeof errback === 'function' ?
287
this._wrap(thenFulfill, thenReject, errback) : thenReject);
289
// If a promise is already fulfilled or rejected, notify the newly added
290
// callbacks by calling fulfill() or reject()
291
if (this._status === 'fulfilled') {
292
this.fulfill(this._result);
293
} else if (this._status === 'rejected') {
294
this.reject(this._result);
301
Wraps the callback in Y.soon to guarantee its asynchronous execution. It
302
also catches exceptions to turn them into rejections and links promises
303
returned from the `then` callback.
306
@param {Function} thenFulfill Fulfillment function of the resolver that
308
@param {Function} thenReject Rejection function of the resolver that
310
@param {Function} fn Callback to wrap
314
_wrap: function (thenFulfill, thenReject, fn) {
315
// callbacks and errbacks only get one argument
316
return function (valueOrReason) {
319
// Promises model exception handling through callbacks
320
// making both synchronous and asynchronous errors behave
323
// Use the argument coming in to the callback/errback from the
324
// resolution of the parent promise.
325
// The function must be called as a normal function, with no
326
// special value for |this|, as per Promises A+
327
result = fn(valueOrReason);
329
// calling return only to stop here
330
return thenReject(e);
333
if (Promise.isPromise(result)) {
334
// Returning a promise from a callback makes the current
335
// promise sync up with the returned promise
336
result.then(thenFulfill, thenReject);
338
// Non-promise return values always trigger resolve()
339
// because callback is affirmative, and errback is
340
// recovery. To continue on the rejection path, errbacks
341
// must return rejected promises or throw.
348
Returns the current status of the Resolver as a string "pending",
349
"fulfilled", or "rejected".
354
getStatus: function () {
359
Executes an array of callbacks from a specified context, passing a set of
363
@param {Function[]} subs The array of subscriber callbacks
364
@param {Any} result Value to pass the callbacks
367
_notify: function (subs, result) {
368
// Since callback lists are reset synchronously, the subs list never
369
// changes after _notify() receives it. Avoid calling Y.soon() for
372
// Calling all callbacks after Y.soon to guarantee
373
// asynchronicity. Because setTimeout can cause unnecessary
374
// delays that *can* become noticeable in some situations
375
// (especially in Node.js)
379
for (i = 0, len = subs.length; i < len; ++i) {
388
Y.Promise.Resolver = Resolver;
390
Abstraction API allowing you to interact with promises or raw values as if they
391
were promises. If a non-promise object is passed in, a new Resolver is created
392
and scheduled to resolve asynchronously with the provided value.
394
In either case, a promise is returned. If either _callback_ or _errback_ are
395
provided, the promise returned is the one returned from calling
396
`promise.then(callback, errback)` on the provided or created promise. If neither
397
are provided, the original promise is returned.
401
@param {Any} promise Promise object or value to wrap in a resolved promise
402
@param {Function} [callback] callback to execute if the promise is resolved
403
@param {Function} [errback] callback to execute if the promise is rejected
406
Y.when = function (promise, callback, errback) {
409
if (!Y.Promise.isPromise(promise)) {
412
promise = new Y.Promise(function (fulfill) {
417
return (callback || errback) ? promise.then(callback, errback) : promise;
419
var slice = [].slice;
422
Returns a new promise that will be resolved when all operations have completed.
423
Takes both any numer of values as arguments. If an argument is a not a promise,
424
it will be wrapped in a new promise, same as in `Y.when()`.
428
@param {Any} operation* Any number of Y.Promise objects or regular JS values
429
@return {Promise} Promise to be fulfilled when all provided promises are
432
Y.batch = function () {
433
var funcs = slice.call(arguments),
434
remaining = funcs.length,
436
length = funcs.length,
439
return new Y.Promise(function (fulfill, reject) {
442
function oneDone(index) {
443
return function (value) {
444
results[index] = value;
448
if (!remaining && allDone.getStatus() !== 'rejected') {
455
return fulfill(results);
458
for (; i < length; i++) {
459
Y.when(funcs[i], oneDone(i), reject);
465
}, '3.13.0', {"requires": ["timers"]});