~ubuntu-branches/ubuntu/utopic/moodle/utopic

« back to all changes in this revision

Viewing changes to lib/yuilib/3.13.0/promise/promise.js

  • Committer: Package Import Robot
  • Author(s): Thijs Kinkhorst
  • Date: 2014-05-12 16:10:38 UTC
  • mfrom: (36.1.3 sid)
  • Revision ID: package-import@ubuntu.com-20140512161038-puyqf65k4e0s8ytz
Tags: 2.6.3-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
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/
 
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 `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.
 
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
/**
 
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.
 
40
 
 
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
 
44
error objects
 
45
 
 
46
<pre><code>
 
47
var fulfilled = new Y.Promise(function (fulfill) {
 
48
    fulfill('I am a fulfilled promise');
 
49
});
 
50
 
 
51
var rejected = new Y.Promise(function (fulfill, reject) {
 
52
    reject(new Error('I am a rejected promise'));
 
53
});
 
54
</code></pre>
 
55
 
 
56
@class Promise
 
57
@constructor
 
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.
 
61
**/
 
62
function Promise(fn) {
 
63
    if (!(this instanceof Promise)) {
 
64
        return new Promise(fn);
 
65
    }
 
66
 
 
67
    var resolver = new Promise.Resolver(this);
 
68
 
 
69
    /**
 
70
    A reference to the resolver object that handles this promise
 
71
 
 
72
    @property _resolver
 
73
    @type Object
 
74
    @private
 
75
    */
 
76
    this._resolver = resolver;
 
77
 
 
78
    fn.call(this, function (value) {
 
79
        resolver.fulfill(value);
 
80
    }, function (reason) {
 
81
        resolver.reject(reason);
 
82
    });
 
83
}
 
84
 
 
85
Y.mix(Promise.prototype, {
 
86
    /**
 
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.
 
92
 
 
93
    Asynchronicity of the callbacks is guaranteed.
 
94
 
 
95
    @method then
 
96
    @param {Function} [callback] function to execute if the promise
 
97
                resolves successfully
 
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
 
101
                "reject" callback
 
102
    **/
 
103
    then: function (callback, errback) {
 
104
        return this._resolver.then(callback, errback);
 
105
    },
 
106
 
 
107
    /**
 
108
    Returns the current status of the operation. Possible results are
 
109
    "pending", "fulfilled", and "rejected".
 
110
 
 
111
    @method getStatus
 
112
    @return {String}
 
113
    **/
 
114
    getStatus: function () {
 
115
        return this._resolver.getStatus();
 
116
    }
 
117
});
 
118
 
 
119
/**
 
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
 
123
method.
 
124
 
 
125
@method isPromise
 
126
@param {Any} obj The object to test
 
127
@return {Boolean} Whether the object is a promise or not
 
128
@static
 
129
**/
 
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';
 
135
};
 
136
 
 
137
Y.Promise = Promise;
 
138
/**
 
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()`).
 
142
 
 
143
@class Promise.Resolver
 
144
@constructor
 
145
@param {Promise} promise The promise instance this resolver will be handling
 
146
**/
 
147
function Resolver(promise) {
 
148
    /**
 
149
    List of success callbacks
 
150
 
 
151
    @property _callbacks
 
152
    @type Array
 
153
    @private
 
154
    **/
 
155
    this._callbacks = [];
 
156
 
 
157
    /**
 
158
    List of failure callbacks
 
159
 
 
160
    @property _errbacks
 
161
    @type Array
 
162
    @private
 
163
    **/
 
164
    this._errbacks = [];
 
165
 
 
166
    /**
 
167
    The promise for this Resolver.
 
168
 
 
169
    @property promise
 
170
    @type Promise
 
171
    **/
 
172
    this.promise = promise;
 
173
 
 
174
    /**
 
175
    The status of the operation. This property may take only one of the following
 
176
    values: 'pending', 'fulfilled' or 'rejected'.
 
177
 
 
178
    @property _status
 
179
    @type String
 
180
    @default 'pending'
 
181
    @private
 
182
    **/
 
183
    this._status = 'pending';
 
184
}
 
185
 
 
186
Y.mix(Resolver.prototype, {
 
187
    /**
 
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.
 
192
 
 
193
    @method fulfill
 
194
    @param {Any} value Value to pass along to the "onFulfilled" subscribers
 
195
    **/
 
196
    fulfill: function (value) {
 
197
        if (this._status === 'pending') {
 
198
            this._result = value;
 
199
        }
 
200
 
 
201
        if (this._status !== 'rejected') {
 
202
            this._notify(this._callbacks, this._result);
 
203
 
 
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 = [];
 
210
 
 
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;
 
215
 
 
216
            this._status = 'fulfilled';
 
217
        }
 
218
    },
 
219
 
 
220
    /**
 
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.
 
225
 
 
226
    @method reject
 
227
    @param {Any} value Value to pass along to the "reject" subscribers
 
228
    **/
 
229
    reject: function (reason) {
 
230
        if (this._status === 'pending') {
 
231
            this._result = reason;
 
232
        }
 
233
 
 
234
        if (this._status !== 'fulfilled') {
 
235
            this._notify(this._errbacks, this._result);
 
236
 
 
237
            // See fulfill()
 
238
            this._callbacks = null;
 
239
            this._errbacks = [];
 
240
 
 
241
            this._status = 'rejected';
 
242
        }
 
243
    },
 
244
 
 
245
    /**
 
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.
 
252
 
 
253
    @method then
 
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
 
260
    **/
 
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,
 
270
 
 
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;
 
275
                thenReject = reject;
 
276
            }),
 
277
 
 
278
            callbackList = this._callbacks || [],
 
279
            errbackList  = this._errbacks  || [];
 
280
 
 
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);
 
288
 
 
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);
 
295
        }
 
296
 
 
297
        return then;
 
298
    },
 
299
 
 
300
    /**
 
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.
 
304
 
 
305
    @method _wrap
 
306
    @param {Function} thenFulfill Fulfillment function of the resolver that
 
307
                        handles this promise
 
308
    @param {Function} thenReject Rejection function of the resolver that
 
309
                        handles this promise
 
310
    @param {Function} fn Callback to wrap
 
311
    @return {Function}
 
312
    @private
 
313
    **/
 
314
    _wrap: function (thenFulfill, thenReject, fn) {
 
315
        // callbacks and errbacks only get one argument
 
316
        return function (valueOrReason) {
 
317
            var result;
 
318
 
 
319
            // Promises model exception handling through callbacks
 
320
            // making both synchronous and asynchronous errors behave
 
321
            // the same way
 
322
            try {
 
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);
 
328
            } catch (e) {
 
329
                // calling return only to stop here
 
330
                return thenReject(e);
 
331
            }
 
332
 
 
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);
 
337
            } else {
 
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.
 
342
                thenFulfill(result);
 
343
            }
 
344
        };
 
345
    },
 
346
 
 
347
    /**
 
348
    Returns the current status of the Resolver as a string "pending",
 
349
    "fulfilled", or "rejected".
 
350
 
 
351
    @method getStatus
 
352
    @return {String}
 
353
    **/
 
354
    getStatus: function () {
 
355
        return this._status;
 
356
    },
 
357
 
 
358
    /**
 
359
    Executes an array of callbacks from a specified context, passing a set of
 
360
    arguments.
 
361
 
 
362
    @method _notify
 
363
    @param {Function[]} subs The array of subscriber callbacks
 
364
    @param {Any} result Value to pass the callbacks
 
365
    @protected
 
366
    **/
 
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
 
370
        // an empty list
 
371
        if (subs.length) {
 
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)
 
376
            Y.soon(function () {
 
377
                var i, len;
 
378
 
 
379
                for (i = 0, len = subs.length; i < len; ++i) {
 
380
                    subs[i](result);
 
381
                }
 
382
            });
 
383
        }
 
384
    }
 
385
 
 
386
}, true);
 
387
 
 
388
Y.Promise.Resolver = Resolver;
 
389
/**
 
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.
 
393
 
 
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.
 
398
 
 
399
@for YUI
 
400
@method when
 
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
 
404
@return {Promise}
 
405
**/
 
406
Y.when = function (promise, callback, errback) {
 
407
    var value;
 
408
 
 
409
    if (!Y.Promise.isPromise(promise)) {
 
410
        value = promise;
 
411
 
 
412
        promise = new Y.Promise(function (fulfill) {
 
413
            fulfill(value);
 
414
        });
 
415
    }
 
416
 
 
417
    return (callback || errback) ? promise.then(callback, errback) : promise;
 
418
};
 
419
var slice = [].slice;
 
420
 
 
421
/**
 
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()`.
 
425
 
 
426
@for YUI
 
427
@method batch
 
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
 
430
                    resolved
 
431
**/
 
432
Y.batch = function () {
 
433
    var funcs     = slice.call(arguments),
 
434
        remaining = funcs.length,
 
435
        i         = 0,
 
436
        length    = funcs.length,
 
437
        results   = [];
 
438
 
 
439
    return new Y.Promise(function (fulfill, reject) {
 
440
        var allDone = this;
 
441
 
 
442
        function oneDone(index) {
 
443
            return function (value) {
 
444
                results[index] = value;
 
445
 
 
446
                remaining--;
 
447
 
 
448
                if (!remaining && allDone.getStatus() !== 'rejected') {
 
449
                    fulfill(results);
 
450
                }
 
451
            };
 
452
        }
 
453
 
 
454
        if (length < 1) {
 
455
            return fulfill(results);
 
456
        }
 
457
 
 
458
        for (; i < length; i++) {
 
459
            Y.when(funcs[i], oneDone(i), reject);
 
460
        }
 
461
    });
 
462
};
 
463
 
 
464
 
 
465
}, '3.13.0', {"requires": ["timers"]});