Skip to content

Commit

Permalink
Revised Deft.Promise::any() to perform a competitive race, added Deft…
Browse files Browse the repository at this point in the history
….Promise::some().

See also: cujojs/when#60

Fixes #63
  • Loading branch information
John Yanarella committed Nov 6, 2012
1 parent 85c0f95 commit 1d92d40
Show file tree
Hide file tree
Showing 6 changed files with 351 additions and 247 deletions.
126 changes: 79 additions & 47 deletions build/deft-debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -1599,7 +1599,7 @@ Ext.define('Deft.mixin.Controllable', {
Copyright (c) 2012 [DeftJS Framework Contributors](http://deftjs.org)
Open source under the [MIT License](http://en.wikipedia.org/wiki/MIT_License).
Promise.when(), all(), any(), map() and reduce() methods adapted from:
Promise.when(), all(), any(), some(), map() and reduce() methods adapted from:
[when.js](https://github.com/cujojs/when)
Copyright (c) B Cavalier & J Hann
Open source under the [MIT License](http://en.wikipedia.org/wiki/MIT_License).
Expand Down Expand Up @@ -1706,58 +1706,90 @@ Ext.define('Deft.promise.Promise', {
});
},
/**
* Returns a new {@link Deft.promise.Promise} that will only resolve once any one of the the specified `promisesOrValues` has resolved.
* The resolution value will be the resolution value of the triggering `promiseOrValue`.
* Initiates a competitive race, returning a new {@link Deft.promise.Promise} that will resolve when any one of the supplied `promisesOrValues`
* have resolved, or will reject when all `promisesOrValues` have rejected or cancelled.
* The resolution value will the first value of `promisesOrValues` to resolve.
*/

any: function(promisesOrValues) {
return this.some(promisesOrValues, 1).then({
success: function(values) {
return values[0];
}
});
},
/**
* Initiates a competitive race, returning a new {@link Deft.promise.Promise} that will resolve when `howMany` of the supplied `promisesOrValues`
* have resolved, or will reject when it becomes impossible for `howMany` to resolve.
* The resolution value will be an Array of the first `howMany` values of `promisesOrValues` to resolve.
*/

some: function(promisesOrValues, howMany) {
return this.when(promisesOrValues).then({
success: function(promisesOrValues) {
var cancelFunction, canceller, complete, deferred, failureFunction, index, progressFunction, promiseOrValue, rejecter, resolver, successFunction, updater, _i, _len;
var cancelFunction, canceller, complete, deferred, errorMessage, failureFunction, index, progressFunction, promiseOrValue, rejecter, remainingToReject, remainingToResolve, resolver, successFunction, updater, values, _i, _len;
values = [];
remainingToResolve = howMany;
remainingToReject = (promisesOrValues.length - remainingToResolve) + 1;
deferred = Ext.create('Deft.promise.Deferred');
updater = function(progress) {
deferred.update(progress);
return progress;
};
resolver = function(value) {
complete();
deferred.resolve(value);
return value;
};
rejecter = function(error) {
complete();
deferred.reject(error);
return error;
};
canceller = function(reason) {
complete();
deferred.cancel(reason);
return reason;
};
complete = function() {
return updater = resolver = rejecter = canceller = Ext.emptyFn;
};
successFunction = function(value) {
return resolver(value);
};
failureFunction = function(value) {
return rejecter(value);
};
progressFunction = function(value) {
return updater(value);
};
cancelFunction = function(value) {
return canceller(value);
};
for (index = _i = 0, _len = promisesOrValues.length; _i < _len; index = ++_i) {
promiseOrValue = promisesOrValues[index];
if (index in promisesOrValues) {
this.when(promiseOrValue).then({
success: successFunction,
failure: failureFunction,
progress: progressFunction,
cancel: cancelFunction
});
if (promisesOrValues.length < howMany) {
deferred.reject(new Error('Too few Promises or values were supplied to obtain the requested number of resolved values.'));
} else {
errorMessage = howMany === 1 ? 'No Promises were resolved.' : 'Too few Promises were resolved.';
updater = function(progress) {
deferred.update(progress);
return progress;
};
resolver = function(value) {
values.push(value);
remainingToResolve--;
if (remainingToResolve === 0) {
complete();
deferred.resolve(values);
}
return value;
};
rejecter = function(error) {
remainingToReject--;
if (remainingToReject === 0) {
complete();
deferred.reject(new Error(errorMessage));
}
return error;
};
canceller = function(reason) {
remainingToReject--;
if (remainingToReject === 0) {
complete();
deferred.reject(new Error(errorMessage));
}
return reason;
};
complete = function() {
return updater = resolver = rejecter = canceller = Ext.emptyFn;
};
successFunction = function(value) {
return resolver(value);
};
failureFunction = function(value) {
return rejecter(value);
};
progressFunction = function(value) {
return updater(value);
};
cancelFunction = function(value) {
return canceller(value);
};
for (index = _i = 0, _len = promisesOrValues.length; _i < _len; index = ++_i) {
promiseOrValue = promisesOrValues[index];
if (index in promisesOrValues) {
this.when(promiseOrValue).then({
success: successFunction,
failure: failureFunction,
progress: progressFunction,
cancel: cancelFunction
});
}
}
}
return deferred.getPromise();
Expand Down
2 changes: 1 addition & 1 deletion build/deft.js

Large diffs are not rendered by default.

107 changes: 61 additions & 46 deletions spec/coffee/Deft/promise/Promise.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ describe( 'Deft.promise.Promise', ->

return @actual.getState() is 'resolved' and wasSpyCalledWith( successCallback, value ) and not wasSpyCalled( failureCallback ) and not wasSpyCalled( progressCallback ) and not wasSpyCalled( cancelCallback )

toRejectWith: ( message ) ->
toRejectWith: ( error ) ->
successCallback = jasmine.createSpy( 'success callback' )
failureCallback = jasmine.createSpy( 'failure callback' )
progressCallback = jasmine.createSpy( 'progress callback' )
Expand All @@ -93,7 +93,7 @@ describe( 'Deft.promise.Promise', ->
cancel: cancelCallback
)

return @actual.getState() is 'rejected' and not wasSpyCalled( successCallback ) and wasSpyCalledWith( failureCallback, message ) and not wasSpyCalled( progressCallback ) and not wasSpyCalled( cancelCallback )
return @actual.getState() is 'rejected' and not wasSpyCalled( successCallback ) and wasSpyCalledWith( failureCallback, error ) and not wasSpyCalled( progressCallback ) and not wasSpyCalled( cancelCallback )

toUpdateWith: ( progress ) ->
successCallback = jasmine.createSpy( 'success callback' )
Expand Down Expand Up @@ -1070,8 +1070,13 @@ describe( 'Deft.promise.Promise', ->

describe( 'with a variety of combinations of values, Deferreds and Promises specified', ->

it( 'should return a resolved Promise when an Array containing any combination of pending Deferreds and/or pending Promises and a value is specified', ->
parameters = [ Ext.create( 'Deft.promise.Deferred' ), Ext.create( 'Deft.promise.Deferred' ).getPromise() ]
it( 'should return a resolved Promise when an Array containing any combination of pending, rejected and/or cancelled Deferreds and/or Promises, and a value is specified', ->
pendingDeferred = Ext.create( 'Deft.promise.Deferred' )
rejectedDeferred = Ext.create( 'Deft.promise.Deferred' )
rejectedDeferred.reject( 'error message' )
cancelledDeferred = Ext.create( 'Deft.promise.Deferred' )
cancelledDeferred.cancel( 'reason' )
parameters = [ pendingDeferred, pendingDeferred.getPromise(), rejectedDeferred, rejectedDeferred.getPromise(), cancelledDeferred, cancelledDeferred.getPromise() ]

for combination in generateCombinations( parameters ).concat( [] )
for permutation in generatePermutations( combination.concat( 'expected result' ) )
Expand All @@ -1090,8 +1095,13 @@ describe( 'Deft.promise.Promise', ->
return
)

it( 'should return a resolved Promise when an Array containing any combination of pending Deferreds and/or pending Promises and a resolved Deferred or Promise is specified', ->
parameters = [ Ext.create( 'Deft.promise.Deferred' ), Ext.create( 'Deft.promise.Deferred' ).getPromise() ]
it( 'should return a resolved Promise when an Array containing any combination of pending, rejected and/or cancelled Deferreds and/or Promises, and a resolved Deferred or Promise is specified', ->
pendingDeferred = Ext.create( 'Deft.promise.Deferred' )
rejectedDeferred = Ext.create( 'Deft.promise.Deferred' )
rejectedDeferred.reject( 'error message' )
cancelledDeferred = Ext.create( 'Deft.promise.Deferred' )
cancelledDeferred.cancel( 'reason' )
parameters = [ pendingDeferred, pendingDeferred.getPromise(), rejectedDeferred, rejectedDeferred.getPromise(), cancelledDeferred, cancelledDeferred.getPromise() ]
resolvedDeferred = Ext.create( 'Deft.promise.Deferred' )
resolvedDeferred.resolve( 'expected result' )

Expand All @@ -1112,28 +1122,35 @@ describe( 'Deft.promise.Promise', ->
return
)

it( 'should return a rejected Promise when an Array containing any combination of pending Deferreds, and/or pending Promises, and a rejected Deferred or Promise is specified', ->
parameters = [ Ext.create( 'Deft.promise.Deferred' ), Ext.create( 'Deft.promise.Deferred' ).getPromise() ]
it( 'should return a rejected Promise when an Array containing any combination of rejected and/or cancelled Deferreds and/or Promises is specified', ->
rejectedDeferred = Ext.create( 'Deft.promise.Deferred' )
rejectedDeferred.reject( 'error message' )
cancelledDeferred = Ext.create( 'Deft.promise.Deferred' )
cancelledDeferred.cancel( 'reason' )
parameters = [ rejectedDeferred, rejectedDeferred.getPromise(), cancelledDeferred, cancelledDeferred.getPromise() ]

for combination in generateCombinations( parameters ).concat( [] )
for permutation in generatePermutations( combination.concat( rejectedDeferred ) )
for combination in generateCombinations( parameters )
for permutation in generatePermutations( combination )
promise = Deft.promise.Promise.any( permutation )

expect( promise ).toBeInstanceOf( 'Deft.promise.Promise' )
expect( promise ).toRejectWith( 'error message' )
expect( promise ).toRejectWith( new Error( 'No Promises were resolved.' ) )

for combination in generateCombinations( parameters ).concat( [] )
for permutation in generatePermutations( combination.concat( rejectedDeferred.getPromise() ) )
for combination in generateCombinations( parameters )
for permutation in generatePermutations( combination )
promise = Deft.promise.Promise.any( permutation )

expect( promise ).toBeInstanceOf( 'Deft.promise.Promise' )
expect( promise ).toRejectWith( 'error message' )
expect( promise ).toRejectWith( new Error( 'No Promises were resolved.' ) )
)

it( 'should return a pending (and immediately updated) Promise when an Array containing any combination of pending Deferreds, and/or pending Promises, and pending (and updated) Deferred or Promise is specified', ->
parameters = [ Ext.create( 'Deft.promise.Deferred' ), Ext.create( 'Deft.promise.Deferred' ).getPromise() ]
it( 'should return a pending (and immediately updated) Promise when an Array containing any combination of pending, rejected and/or cancelled Deferreds and/or Promises, and a pending (and updated) Deferred or Promise is specified', ->
pendingDeferred = Ext.create( 'Deft.promise.Deferred' )
rejectedDeferred = Ext.create( 'Deft.promise.Deferred' )
rejectedDeferred.reject( 'error message' )
cancelledDeferred = Ext.create( 'Deft.promise.Deferred' )
cancelledDeferred.cancel( 'reason' )
parameters = [ pendingDeferred, pendingDeferred.getPromise(), rejectedDeferred, rejectedDeferred.getPromise(), cancelledDeferred, cancelledDeferred.getPromise() ]
updatedDeferred = Ext.create( 'Deft.promise.Deferred' )
updatedDeferred.update( 'progress' )

Expand All @@ -1152,28 +1169,13 @@ describe( 'Deft.promise.Promise', ->
expect( promise ).toUpdateWith( 'progress' )
)

it( 'should return a cancelled Promise when an Array containing any combination of pending Deferreds, and/or pending Promises, and a cancelled Deferred or Promise is specified', ->
parameters = [ Ext.create( 'Deft.promise.Deferred' ), Ext.create( 'Deft.promise.Deferred' ).getPromise() ]
it( 'should return a resolved Promise when an Array containing any combination of pending, rejected and/or cancelled Deferreds and/or Promises, and a pending Deferred or Promise is specified that is later resolved', ->
pendingDeferred = Ext.create( 'Deft.promise.Deferred' )
rejectedDeferred = Ext.create( 'Deft.promise.Deferred' )
rejectedDeferred.reject( 'error message' )
cancelledDeferred = Ext.create( 'Deft.promise.Deferred' )
cancelledDeferred.cancel( 'reason' )

for combination in generateCombinations( parameters ).concat( [] )
for permutation in generatePermutations( combination.concat( cancelledDeferred ) )
promise = Deft.promise.Promise.any( permutation )

expect( promise ).toBeInstanceOf( 'Deft.promise.Promise' )
expect( promise ).toCancelWith( 'reason' )

for combination in generateCombinations( parameters ).concat( [] )
for permutation in generatePermutations( combination.concat( cancelledDeferred.getPromise() ) )
promise = Deft.promise.Promise.any( permutation )

expect( promise ).toBeInstanceOf( 'Deft.promise.Promise' )
expect( promise ).toCancelWith( 'reason' )
)

it( 'should return a resolved Promise when an Array containing any combination of pending Deferreds and/or pending Promises, and a pending Deferred or Promise is specified that is later resolved', ->
parameters = [ Ext.create( 'Deft.promise.Deferred' ), Ext.create( 'Deft.promise.Deferred' ).getPromise() ]
parameters = [ pendingDeferred, pendingDeferred.getPromise(), rejectedDeferred, rejectedDeferred.getPromise(), cancelledDeferred, cancelledDeferred.getPromise() ]
placeholder = {}

for combination in generateCombinations( parameters ).concat( [] )
Expand Down Expand Up @@ -1209,8 +1211,12 @@ describe( 'Deft.promise.Promise', ->
return
)

it( 'should return a rejected Promise when an Array containing any combination of pending Deferreds and/or pending Promises, and a pending Deferred or Promise is specified that is later rejected', ->
parameters = [ Ext.create( 'Deft.promise.Deferred' ), Ext.create( 'Deft.promise.Deferred' ).getPromise() ]
it( 'should return a rejected Promise when an Array containing any combination of rejected and/or cancelled Deferreds and/or Promises, and a pending Deferred or Promise is specified that is later rejected', ->
rejectedDeferred = Ext.create( 'Deft.promise.Deferred' )
rejectedDeferred.reject( 'error message' )
cancelledDeferred = Ext.create( 'Deft.promise.Deferred' )
cancelledDeferred.cancel( 'reason' )
parameters = [ rejectedDeferred, rejectedDeferred.getPromise(), cancelledDeferred, cancelledDeferred.getPromise() ]
placeholder = {}

for combination in generateCombinations( parameters ).concat( [] )
Expand All @@ -1226,7 +1232,7 @@ describe( 'Deft.promise.Promise', ->

pendingDeferred.reject( 'error message' )

expect( promise ).toRejectWith( 'error message' )
expect( promise ).toRejectWith( new Error( 'No Promises were resolved.' ) )

for combination in generateCombinations( parameters ).concat( [] )
for permutation in generatePermutations( combination.concat( placeholder ) )
Expand All @@ -1241,13 +1247,18 @@ describe( 'Deft.promise.Promise', ->

pendingDeferred.reject( 'error message' )

expect( promise ).toRejectWith( 'error message' )
expect( promise ).toRejectWith( new Error( 'No Promises were resolved.' ) )

return
)

it( 'should return a pending (and later updated) when an Array containing any combination of pending Deferreds and/or pending Promises, and a pending Deferred or Promise is specified that is later updated', ->
parameters = [ Ext.create( 'Deft.promise.Deferred' ), Ext.create( 'Deft.promise.Deferred' ).getPromise() ]
it( 'should return a pending (and later updated) when an Array containing any combination of pending, rejected and/or Deferreds and/or Promises, and a pending Deferred or Promise is specified that is later updated', ->
pendingDeferred = Ext.create( 'Deft.promise.Deferred' )
rejectedDeferred = Ext.create( 'Deft.promise.Deferred' )
rejectedDeferred.reject( 'error message' )
cancelledDeferred = Ext.create( 'Deft.promise.Deferred' )
cancelledDeferred.cancel( 'reason' )
parameters = [ pendingDeferred, pendingDeferred.getPromise(), rejectedDeferred, rejectedDeferred.getPromise(), cancelledDeferred, cancelledDeferred.getPromise() ]
placeholder = {}

for combination in generateCombinations( parameters ).concat( [] )
Expand Down Expand Up @@ -1283,8 +1294,12 @@ describe( 'Deft.promise.Promise', ->
return
)

it( 'should return a cancelled Promise when an Array containing any combination of pending Deferreds and/or pending Promises, and a pending Deferred or Promise is specified that is later cancelled', ->
parameters = [ Ext.create( 'Deft.promise.Deferred' ), Ext.create( 'Deft.promise.Deferred' ).getPromise() ]
it( 'should return a rejected Promise when an Array containing any combination of rejected and/or cancelled Deferreds and/or Promises, and a pending Deferred or Promise is specified that is later cancelled', ->
rejectedDeferred = Ext.create( 'Deft.promise.Deferred' )
rejectedDeferred.reject( 'error message' )
cancelledDeferred = Ext.create( 'Deft.promise.Deferred' )
cancelledDeferred.cancel( 'reason' )
parameters = [ rejectedDeferred, rejectedDeferred.getPromise(), cancelledDeferred, cancelledDeferred.getPromise() ]
placeholder = {}

for combination in generateCombinations( parameters ).concat( [] )
Expand All @@ -1300,7 +1315,7 @@ describe( 'Deft.promise.Promise', ->

pendingDeferred.cancel( 'reason' )

expect( promise ).toCancelWith( 'reason' )
expect( promise ).toRejectWith( new Error( 'No Promises were resolved.' ) )

for combination in generateCombinations( parameters ).concat( [] )
for permutation in generatePermutations( combination.concat( placeholder ) )
Expand All @@ -1315,7 +1330,7 @@ describe( 'Deft.promise.Promise', ->

pendingDeferred.cancel( 'reason' )

expect( promise ).toCancelWith( 'reason' )
expect( promise ).toRejectWith( new Error( 'No Promises were resolved.' ) )

return
)
Expand Down
Loading

0 comments on commit 1d92d40

Please sign in to comment.