mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-01-12 22:45:52 +08:00
feat($q): add streamlined ES6-style interface for using $q
This potentially helps lead the way towards a more performant fly-weight implementation, as discussed earlier in the year. Using a constructor means we can put things in the prototype chain, and essentially treat $q as a Promise class, and reuse methods as appropriate. Short of that, I feel this style is slightly more convenient and streamlined, compared with the older API. Closes #8311 Closes #6427 (I know it's not really the solution asked for in #6427, sorry!)
This commit is contained in:
83
src/ng/q.js
83
src/ng/q.js
@@ -8,6 +8,46 @@
|
||||
* @description
|
||||
* A promise/deferred implementation inspired by [Kris Kowal's Q](https://github.com/kriskowal/q).
|
||||
*
|
||||
* $q can be used in two fashions --- One, which is more similar to Kris Kowal's Q or jQuery's Deferred
|
||||
* implementations, the other resembles ES6 promises to some degree.
|
||||
*
|
||||
* # $q constructor
|
||||
*
|
||||
* The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
|
||||
* function as the first argument). This is similar to the native Promise implementation from ES6 Harmony,
|
||||
* see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
|
||||
*
|
||||
* While the constructor-style use is supported, not all of the supporting methods from Harmony promises are
|
||||
* available yet.
|
||||
*
|
||||
* It can be used like so:
|
||||
*
|
||||
* ```js
|
||||
* return $q(function(resolve, reject) {
|
||||
* // perform some asynchronous operation, resolve or reject the promise when appropriate.
|
||||
* setInterval(function() {
|
||||
* if (pollStatus > 0) {
|
||||
* resolve(polledValue);
|
||||
* } else if (pollStatus < 0) {
|
||||
* reject(polledValue);
|
||||
* } else {
|
||||
* pollStatus = pollAgain(function(value) {
|
||||
* polledValue = value;
|
||||
* });
|
||||
* }
|
||||
* }, 10000);
|
||||
* }).
|
||||
* then(function(value) {
|
||||
* // handle success
|
||||
* }, function(reason) {
|
||||
* // handle failure
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Note, progress/notify callbacks are not currently supported via the ES6-style interface.
|
||||
*
|
||||
* However, the more traditional CommonJS style usage is still available, and documented below.
|
||||
*
|
||||
* [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
|
||||
* interface for interacting with an object that represents the result of an action that is
|
||||
* performed asynchronously, and may or may not be finished at any given point in time.
|
||||
@@ -54,7 +94,6 @@
|
||||
* For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
|
||||
* section on serial or parallel joining of promises.
|
||||
*
|
||||
*
|
||||
* # The Deferred API
|
||||
*
|
||||
* A new instance of deferred is constructed by calling `$q.defer()`.
|
||||
@@ -163,6 +202,12 @@
|
||||
* expect(resolvedValue).toEqual(123);
|
||||
* }));
|
||||
* ```
|
||||
*
|
||||
* @param {function(function, function)} resolver Function which is responsible for resolving or
|
||||
* rejecting the newly created promise. The first parameteter is a function which resolves the
|
||||
* promise, the second parameter is a function which rejects the promise.
|
||||
*
|
||||
* @returns {Promise} The newly created promise.
|
||||
*/
|
||||
function $QProvider() {
|
||||
|
||||
@@ -519,10 +564,36 @@ function qFactory(nextTick, exceptionHandler) {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
return {
|
||||
defer: defer,
|
||||
reject: reject,
|
||||
when: when,
|
||||
all: all
|
||||
var $Q = function Q(resolver) {
|
||||
if (!isFunction(resolver)) {
|
||||
// TODO(@caitp): minErr this
|
||||
throw new TypeError('Expected resolverFn');
|
||||
}
|
||||
|
||||
if (!(this instanceof Q)) {
|
||||
// More useful when $Q is the Promise itself.
|
||||
return new Q(resolver);
|
||||
}
|
||||
|
||||
var deferred = defer();
|
||||
|
||||
function resolveFn(value) {
|
||||
deferred.resolve(value);
|
||||
}
|
||||
|
||||
function rejectFn(reason) {
|
||||
deferred.reject(reason);
|
||||
}
|
||||
|
||||
resolver(resolveFn, rejectFn);
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
$Q.defer = defer;
|
||||
$Q.reject = reject;
|
||||
$Q.when = when;
|
||||
$Q.all = all;
|
||||
|
||||
return $Q;
|
||||
}
|
||||
|
||||
599
test/ng/qSpec.js
599
test/ng/qSpec.js
@@ -196,6 +196,605 @@ describe('q', function() {
|
||||
});
|
||||
|
||||
|
||||
describe('$Q', function() {
|
||||
var resolve, reject, resolve2, reject2;
|
||||
var createPromise = function() {
|
||||
return q(function(resolveFn, rejectFn) {
|
||||
if (resolve === null) {
|
||||
resolve = resolveFn;
|
||||
reject = rejectFn;
|
||||
} else if (resolve2 === null) {
|
||||
resolve2 = resolveFn;
|
||||
reject2 = rejectFn;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(function() {
|
||||
resolve = reject = resolve2 = reject2 = null;
|
||||
});
|
||||
|
||||
it('should return a Promise', function() {
|
||||
var promise = q(noop);
|
||||
expect(typeof promise.then).toBe('function');
|
||||
expect(typeof promise.catch).toBe('function');
|
||||
expect(typeof promise.finally).toBe('function');
|
||||
});
|
||||
|
||||
|
||||
describe('resolve', function() {
|
||||
it('should fulfill the promise and execute all success callbacks in the registration order',
|
||||
function() {
|
||||
var promise = createPromise();
|
||||
promise.then(success(1), error());
|
||||
promise.then(success(2), error());
|
||||
expect(logStr()).toBe('');
|
||||
|
||||
resolve('foo');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('success1(foo)->foo; success2(foo)->foo');
|
||||
});
|
||||
|
||||
|
||||
it('should do nothing if a promise was previously resolved', function() {
|
||||
var promise = createPromise();
|
||||
promise.then(success(), error());
|
||||
expect(logStr()).toBe('');
|
||||
|
||||
resolve('foo');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('success(foo)->foo');
|
||||
|
||||
log = [];
|
||||
resolve('bar');
|
||||
reject('baz');
|
||||
expect(mockNextTick.queue.length).toBe(0);
|
||||
expect(logStr()).toBe('');
|
||||
});
|
||||
|
||||
|
||||
it('should do nothing if a promise was previously rejected', function() {
|
||||
var promise = createPromise();
|
||||
promise.then(success(), error());
|
||||
expect(logStr()).toBe('');
|
||||
|
||||
reject('foo');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('error(foo)->reject(foo)');
|
||||
|
||||
log = [];
|
||||
resolve('bar');
|
||||
reject('baz');
|
||||
expect(mockNextTick.queue.length).toBe(0);
|
||||
expect(logStr()).toBe('');
|
||||
});
|
||||
|
||||
|
||||
it('should allow deferred resolution with a new promise', function() {
|
||||
var promise = createPromise();
|
||||
|
||||
promise.then(success(), error());
|
||||
|
||||
resolve(createPromise());
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('');
|
||||
|
||||
resolve2('foo');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('success(foo)->foo');
|
||||
});
|
||||
|
||||
|
||||
it('should call the callback in the next turn', function() {
|
||||
var promise = createPromise();
|
||||
promise.then(success());
|
||||
expect(logStr()).toBe('');
|
||||
|
||||
resolve('foo');
|
||||
expect(logStr()).toBe('');
|
||||
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('success(foo)->foo');
|
||||
});
|
||||
|
||||
|
||||
it('should not break if a callbacks registers another callback', function() {
|
||||
var promise = createPromise();
|
||||
promise.then(function() {
|
||||
log.push('outer');
|
||||
promise.then(function() {
|
||||
log.push('inner');
|
||||
});
|
||||
});
|
||||
|
||||
resolve('foo');
|
||||
expect(logStr()).toBe('');
|
||||
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('outer; inner');
|
||||
});
|
||||
|
||||
|
||||
it('should not break if a callbacks tries to resolve the deferred again', function() {
|
||||
var promise = createPromise();
|
||||
promise.then(function(val) {
|
||||
log.push('then1(' + val + ')->resolve(bar)');
|
||||
deferred.resolve('bar'); // nop
|
||||
});
|
||||
|
||||
promise.then(success(2));
|
||||
|
||||
resolve('foo');
|
||||
expect(logStr()).toBe('');
|
||||
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('then1(foo)->resolve(bar); success2(foo)->foo');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('reject', function() {
|
||||
it('should reject the promise and execute all error callbacks in the registration order',
|
||||
function() {
|
||||
var promise = createPromise();
|
||||
promise.then(success(), error(1));
|
||||
promise.then(success(), error(2));
|
||||
expect(logStr()).toBe('');
|
||||
|
||||
reject('foo');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('error1(foo)->reject(foo); error2(foo)->reject(foo)');
|
||||
});
|
||||
|
||||
|
||||
it('should do nothing if a promise was previously resolved', function() {
|
||||
var promise = createPromise();
|
||||
promise.then(success(1), error(1));
|
||||
expect(logStr()).toBe('');
|
||||
|
||||
resolve('foo');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('success1(foo)->foo');
|
||||
|
||||
log = [];
|
||||
reject('bar');
|
||||
resolve('baz');
|
||||
expect(mockNextTick.queue.length).toBe(0);
|
||||
expect(logStr()).toBe('');
|
||||
|
||||
promise.then(success(2), error(2));
|
||||
expect(logStr()).toBe('');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('success2(foo)->foo');
|
||||
});
|
||||
|
||||
|
||||
it('should do nothing if a promise was previously rejected', function() {
|
||||
var promise = createPromise();
|
||||
promise.then(success(1), error(1));
|
||||
expect(logStr()).toBe('');
|
||||
|
||||
reject('foo');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('error1(foo)->reject(foo)');
|
||||
|
||||
log = [];
|
||||
reject('bar');
|
||||
resolve('baz');
|
||||
expect(mockNextTick.queue.length).toBe(0);
|
||||
expect(logStr()).toBe('');
|
||||
|
||||
promise.then(success(2), error(2));
|
||||
expect(logStr()).toBe('');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('error2(foo)->reject(foo)');
|
||||
});
|
||||
|
||||
|
||||
it('should not defer rejection with a new promise', function() {
|
||||
var promise = createPromise();
|
||||
promise.then(success(), error());
|
||||
|
||||
reject(createPromise());
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('error({})->reject({})');
|
||||
});
|
||||
|
||||
|
||||
it('should call the error callback in the next turn', function() {
|
||||
var promise = createPromise();
|
||||
promise.then(success(), error());
|
||||
expect(logStr()).toBe('');
|
||||
|
||||
reject('foo');
|
||||
expect(logStr()).toBe('');
|
||||
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('error(foo)->reject(foo)');
|
||||
});
|
||||
|
||||
|
||||
it('should support non-bound execution', function() {
|
||||
var promise = createPromise();
|
||||
promise.then(success(), error());
|
||||
reject('detached');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('error(detached)->reject(detached)');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('promise', function() {
|
||||
describe('then', function() {
|
||||
it('should allow registration of a success callback without an errback or progressback ' +
|
||||
'and resolve', function() {
|
||||
var promise = createPromise();
|
||||
promise.then(success());
|
||||
resolve('foo');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('success(foo)->foo');
|
||||
});
|
||||
|
||||
|
||||
it('should allow registration of a success callback without an errback and reject',
|
||||
function() {
|
||||
var promise = createPromise();
|
||||
promise.then(success());
|
||||
reject('foo');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('');
|
||||
});
|
||||
|
||||
|
||||
it('should allow registration of an errback without a success or progress callback and ' +
|
||||
' reject', function() {
|
||||
var promise = createPromise();
|
||||
promise.then(null, error());
|
||||
reject('oops!');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('error(oops!)->reject(oops!)');
|
||||
});
|
||||
|
||||
|
||||
it('should allow registration of an errback without a success callback and resolve',
|
||||
function() {
|
||||
var promise = createPromise();
|
||||
promise.then(null, error());
|
||||
resolve('done');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('');
|
||||
});
|
||||
|
||||
|
||||
it('should allow registration of an progressback without a success callback and resolve',
|
||||
function() {
|
||||
var promise = createPromise();
|
||||
promise.then(null, null, progress());
|
||||
resolve('done');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('');
|
||||
});
|
||||
|
||||
|
||||
it('should allow registration of an progressback without a error callback and reject',
|
||||
function() {
|
||||
var promise = createPromise();
|
||||
promise.then(null, null, progress());
|
||||
reject('oops!');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('');
|
||||
});
|
||||
|
||||
|
||||
it('should resolve all callbacks with the original value', function() {
|
||||
var promise = createPromise();
|
||||
promise.then(success('A', 'aVal'), error(), progress());
|
||||
promise.then(success('B', 'bErr', true), error(), progress());
|
||||
promise.then(success('C', q.reject('cReason')), error(), progress());
|
||||
promise.then(success('D', q.reject('dReason'), true), error(), progress());
|
||||
promise.then(success('E', 'eVal'), error(), progress());
|
||||
|
||||
expect(logStr()).toBe('');
|
||||
resolve('yup');
|
||||
mockNextTick.flush();
|
||||
expect(log).toEqual(['successA(yup)->aVal',
|
||||
'successB(yup)->throw(bErr)',
|
||||
'successC(yup)->{}',
|
||||
'successD(yup)->throw({})',
|
||||
'successE(yup)->eVal']);
|
||||
});
|
||||
|
||||
|
||||
it('should reject all callbacks with the original reason', function() {
|
||||
var promise = createPromise();
|
||||
promise.then(success(), error('A', 'aVal'), progress());
|
||||
promise.then(success(), error('B', 'bEr', true), progress());
|
||||
promise.then(success(), error('C', q.reject('cReason')), progress());
|
||||
promise.then(success(), error('D', 'dVal'), progress());
|
||||
|
||||
expect(logStr()).toBe('');
|
||||
reject('noo!');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('errorA(noo!)->aVal; errorB(noo!)->throw(bEr); errorC(noo!)->{}; errorD(noo!)->dVal');
|
||||
});
|
||||
|
||||
|
||||
it('should propagate resolution and rejection between dependent promises', function() {
|
||||
var promise = createPromise();
|
||||
promise.then(success(1, 'x'), error('1')).
|
||||
then(success(2, 'y', true), error('2')).
|
||||
then(success(3), error(3, 'z', true)).
|
||||
then(success(4), error(4, 'done')).
|
||||
then(success(5), error(5));
|
||||
|
||||
expect(logStr()).toBe('');
|
||||
resolve('sweet!');
|
||||
mockNextTick.flush();
|
||||
expect(log).toEqual(['success1(sweet!)->x',
|
||||
'success2(x)->throw(y)',
|
||||
'error3(y)->throw(z)',
|
||||
'error4(z)->done',
|
||||
'success5(done)->done']);
|
||||
});
|
||||
|
||||
|
||||
it('should reject a derived promise if an exception is thrown while resolving its parent',
|
||||
function() {
|
||||
var promise = createPromise();
|
||||
promise.then(success(1, 'oops', true), error(1)).
|
||||
then(success(2), error(2));
|
||||
resolve('done!');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('success1(done!)->throw(oops); error2(oops)->reject(oops)');
|
||||
});
|
||||
|
||||
|
||||
it('should reject a derived promise if an exception is thrown while rejecting its parent',
|
||||
function() {
|
||||
var promise = createPromise();
|
||||
promise.then(null, error(1, 'oops', true)).
|
||||
then(success(2), error(2));
|
||||
reject('timeout');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('error1(timeout)->throw(oops); error2(oops)->reject(oops)');
|
||||
});
|
||||
|
||||
|
||||
it('should call success callback in the next turn even if promise is already resolved',
|
||||
function() {
|
||||
var promise = createPromise();
|
||||
resolve('done!');
|
||||
|
||||
promise.then(success());
|
||||
expect(logStr()).toBe('');
|
||||
|
||||
mockNextTick.flush();
|
||||
expect(log).toEqual(['success(done!)->done!']);
|
||||
});
|
||||
|
||||
|
||||
it('should call error callback in the next turn even if promise is already rejected',
|
||||
function() {
|
||||
var promise = createPromise();
|
||||
reject('oops!');
|
||||
|
||||
promise.then(null, error());
|
||||
expect(logStr()).toBe('');
|
||||
|
||||
mockNextTick.flush();
|
||||
expect(log).toEqual(['error(oops!)->reject(oops!)']);
|
||||
});
|
||||
|
||||
it('should forward success resolution when success callbacks are not functions', function() {
|
||||
var promise = createPromise();
|
||||
resolve('yay!');
|
||||
|
||||
promise.then(1).
|
||||
then(null).
|
||||
then({}).
|
||||
then('gah!').
|
||||
then([]).
|
||||
then(success());
|
||||
|
||||
expect(logStr()).toBe('');
|
||||
|
||||
mockNextTick.flush();
|
||||
expect(log).toEqual(['success(yay!)->yay!']);
|
||||
});
|
||||
|
||||
it('should forward error resolution when error callbacks are not functions', function() {
|
||||
var promise = createPromise();
|
||||
reject('oops!');
|
||||
|
||||
promise.then(null, 1).
|
||||
then(null, null).
|
||||
then(null, {}).
|
||||
then(null, 'gah!').
|
||||
then(null, []).
|
||||
then(null, error());
|
||||
|
||||
expect(logStr()).toBe('');
|
||||
|
||||
mockNextTick.flush();
|
||||
expect(log).toEqual(['error(oops!)->reject(oops!)']);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('finally', function() {
|
||||
it('should not take an argument',
|
||||
function() {
|
||||
var promise = createPromise();
|
||||
promise['finally'](fin(1));
|
||||
resolve('foo');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('finally1()');
|
||||
});
|
||||
|
||||
describe("when the promise is fulfilled", function () {
|
||||
it('should call the callback',
|
||||
function() {
|
||||
var promise = createPromise();
|
||||
promise.then(success(1))['finally'](fin(1));
|
||||
resolve('foo');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('success1(foo)->foo; finally1()');
|
||||
});
|
||||
|
||||
it('should fulfill with the original value',
|
||||
function() {
|
||||
var promise = createPromise();
|
||||
promise['finally'](fin('B', 'b'), error('B')).
|
||||
then(success('BB', 'bb'), error('BB'));
|
||||
resolve('RESOLVED_VAL');
|
||||
mockNextTick.flush();
|
||||
expect(log).toEqual(['finallyB()->b',
|
||||
'successBB(RESOLVED_VAL)->bb']);
|
||||
});
|
||||
|
||||
|
||||
it('should fulfill with the original value (larger test)',
|
||||
function() {
|
||||
var promise = createPromise();
|
||||
promise.then(success('A', 'a'), error('A'));
|
||||
promise['finally'](fin('B', 'b'), error('B')).
|
||||
then(success('BB', 'bb'), error('BB'));
|
||||
promise.then(success('C', 'c'), error('C'))['finally'](fin('CC', 'IGNORED'))
|
||||
.then(success('CCC', 'cc'), error('CCC'))
|
||||
.then(success('CCCC', 'ccc'), error('CCCC'));
|
||||
resolve('RESOLVED_VAL');
|
||||
mockNextTick.flush();
|
||||
|
||||
expect(log).toEqual(['successA(RESOLVED_VAL)->a',
|
||||
'finallyB()->b',
|
||||
'successC(RESOLVED_VAL)->c',
|
||||
'successBB(RESOLVED_VAL)->bb',
|
||||
'finallyCC()->IGNORED',
|
||||
'successCCC(c)->cc',
|
||||
'successCCCC(cc)->ccc']);
|
||||
});
|
||||
|
||||
describe("when the callback returns a promise", function() {
|
||||
describe("that is fulfilled", function() {
|
||||
it("should fulfill with the original reason after that promise resolves",
|
||||
function () {
|
||||
var promise = createPromise();
|
||||
var promise2 = createPromise();
|
||||
resolve2('bar');
|
||||
|
||||
promise['finally'](fin(1, promise))
|
||||
.then(success(2));
|
||||
|
||||
resolve('foo');
|
||||
mockNextTick.flush();
|
||||
|
||||
expect(logStr()).toBe('finally1()->{}; success2(foo)->foo');
|
||||
});
|
||||
});
|
||||
|
||||
describe("that is rejected", function() {
|
||||
it("should reject with this new rejection reason",
|
||||
function () {
|
||||
var promise = createPromise();
|
||||
var promise2 = createPromise();
|
||||
reject2('bar');
|
||||
promise['finally'](fin(1, promise2))
|
||||
.then(success(2), error(1));
|
||||
resolve('foo');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('finally1()->{}; error1(bar)->reject(bar)');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("when the callback throws an exception", function() {
|
||||
it("should reject with this new exception", function() {
|
||||
var promise = createPromise();
|
||||
promise['finally'](fin(1, "exception", true))
|
||||
.then(success(1), error(2));
|
||||
resolve('foo');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('finally1()->throw(exception); error2(exception)->reject(exception)');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe("when the promise is rejected", function () {
|
||||
it("should call the callback", function () {
|
||||
var promise = createPromise();
|
||||
promise['finally'](fin(1))
|
||||
.then(success(2), error(1));
|
||||
reject('foo');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('finally1(); error1(foo)->reject(foo)');
|
||||
});
|
||||
|
||||
it('should reject with the original reason', function() {
|
||||
var promise = createPromise();
|
||||
promise['finally'](fin(1), "hello")
|
||||
.then(success(2), error(2));
|
||||
reject('original');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('finally1(); error2(original)->reject(original)');
|
||||
});
|
||||
|
||||
describe("when the callback returns a promise", function() {
|
||||
describe("that is fulfilled", function() {
|
||||
it("should reject with the original reason after that promise resolves", function () {
|
||||
var promise = createPromise();
|
||||
var promise2 = createPromise();
|
||||
resolve2('bar');
|
||||
promise['finally'](fin(1, promise2))
|
||||
.then(success(2), error(2));
|
||||
reject('original');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('finally1()->{}; error2(original)->reject(original)');
|
||||
});
|
||||
});
|
||||
|
||||
describe("that is rejected", function () {
|
||||
it("should reject with the new reason", function() {
|
||||
var promise = createPromise();
|
||||
var promise2 = createPromise();
|
||||
reject2('bar');
|
||||
promise['finally'](fin(1, promise2))
|
||||
.then(success(2), error(1));
|
||||
resolve('foo');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('finally1()->{}; error1(bar)->reject(bar)');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the callback throws an exception", function() {
|
||||
it("should reject with this new exception", function() {
|
||||
var promise = createPromise();
|
||||
promise['finally'](fin(1, "exception", true))
|
||||
.then(success(1), error(2));
|
||||
resolve('foo');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('finally1()->throw(exception); error2(exception)->reject(exception)');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('catch', function() {
|
||||
it('should be a shorthand for defining promise error handlers', function() {
|
||||
var promise = createPromise();
|
||||
promise['catch'](error(1)).then(null, error(2));
|
||||
reject('foo');
|
||||
mockNextTick.flush();
|
||||
expect(logStr()).toBe('error1(foo)->reject(foo); error2(foo)->reject(foo)');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('defer', function() {
|
||||
it('should create a new deferred', function() {
|
||||
expect(deferred.promise).toBeDefined();
|
||||
|
||||
Reference in New Issue
Block a user