Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Adds always and chain methods to deferreds.

  • Loading branch information...
commit bb99899ca0de93dd12f5a53f409ff6f72bfcf94c 1 parent 75a4bc4
@jaubourg jaubourg authored
Showing with 282 additions and 128 deletions.
  1. +30 −2 src/deferred.js
  2. +252 −126 test/unit/deferred.js
View
32 src/deferred.js
@@ -1,7 +1,7 @@
(function( jQuery ) {
var // Promise methods
- promiseMethods = "then done fail isResolved isRejected promise".split( " " ),
+ promiseMethods = "done fail isResolved isRejected promise then always chain".split( " " ),
// Static reference to slice
sliceDeferred = [].slice;
@@ -100,10 +100,38 @@ jQuery.extend({
deferred.done( doneCallbacks ).fail( failCallbacks );
return this;
},
+ always: function() {
+ return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments );
+ },
fail: failDeferred.done,
rejectWith: failDeferred.resolveWith,
reject: failDeferred.resolve,
isRejected: failDeferred.isResolved,
+ // Chain
+ chain: function( fnDone, fnFail ) {
+ return jQuery.Deferred(function( newDefer ) {
+ jQuery.each( {
+ done: [ fnDone, "resolve" ],
+ fail: [ fnFail, "reject" ]
+ }, function( handler, data ) {
+ var fn = data[ 0 ],
+ action = data[ 1 ],
+ returned;
+ if ( jQuery.isFunction( fn ) ) {
+ deferred[ handler ](function() {
+ returned = fn.apply( this, arguments );
+ if ( jQuery.isFunction( returned.promise ) ) {
+ returned.promise().then( newDefer.resolve, newDefer.reject );
+ } else {
+ newDefer[ action ]( returned );
+ }
+ });
+ } else {
+ deferred[ handler ]( newDefer[ action ] );
+ }
+ } );
+ }).promise();
+ },
// Get a promise for this deferred
// If obj is provided, the promise aspect is added to the object
promise: function( obj ) {
@@ -169,4 +197,4 @@ jQuery.extend({
}
});
-})( jQuery );
+})( jQuery );
View
378 test/unit/deferred.js
@@ -1,153 +1,273 @@
module("deferred", { teardown: moduleTeardown });
-test("jQuery._Deferred()", function() {
+jQuery.each( [ "", " - new operator" ], function( _, withNew ) {
- expect( 11 );
+ function createDeferred() {
+ return withNew ? new jQuery._Deferred() : jQuery._Deferred();
+ }
- var deferred,
- object,
- test;
+ test("jQuery._Deferred" + withNew, function() {
- deferred = jQuery._Deferred();
+ expect( 11 );
- test = false;
+ var deferred,
+ object,
+ test;
- deferred.done( function( value ) {
- equals( value , "value" , "Test pre-resolve callback" );
- test = true;
- } );
+ deferred = createDeferred();
- deferred.resolve( "value" );
-
- ok( test , "Test pre-resolve callbacks called right away" );
+ test = false;
- test = false;
+ deferred.done( function( value ) {
+ equals( value , "value" , "Test pre-resolve callback" );
+ test = true;
+ } );
- deferred.done( function( value ) {
- equals( value , "value" , "Test post-resolve callback" );
- test = true;
- } );
+ deferred.resolve( "value" );
- ok( test , "Test post-resolve callbacks called right away" );
+ ok( test , "Test pre-resolve callbacks called right away" );
- deferred.cancel();
+ test = false;
- test = true;
+ deferred.done( function( value ) {
+ equals( value , "value" , "Test post-resolve callback" );
+ test = true;
+ } );
- deferred.done( function() {
- ok( false , "Cancel was ignored" );
- test = false;
- } );
+ ok( test , "Test post-resolve callbacks called right away" );
- ok( test , "Test cancel" );
+ deferred.cancel();
- deferred = jQuery._Deferred().resolve();
+ test = true;
- try {
deferred.done( function() {
- throw "Error";
- } , function() {
- ok( true , "Test deferred do not cancel on exception" );
+ ok( false , "Cancel was ignored" );
+ test = false;
} );
- } catch( e ) {
- strictEqual( e , "Error" , "Test deferred propagates exceptions");
- deferred.done();
- }
- test = "";
- deferred = jQuery._Deferred().done( function() {
+ ok( test , "Test cancel" );
- test += "A";
+ deferred = createDeferred().resolve();
- }, function() {
+ try {
+ deferred.done( function() {
+ throw "Error";
+ } , function() {
+ ok( true , "Test deferred do not cancel on exception" );
+ } );
+ } catch( e ) {
+ strictEqual( e , "Error" , "Test deferred propagates exceptions");
+ deferred.done();
+ }
- test += "B";
+ test = "";
+ deferred = createDeferred().done( function() {
- } ).resolve();
+ test += "A";
- strictEqual( test , "AB" , "Test multiple done parameters" );
+ }, function() {
+
+ test += "B";
- test = "";
+ } ).resolve();
- deferred.done( function() {
+ strictEqual( test , "AB" , "Test multiple done parameters" );
+
+ test = "";
deferred.done( function() {
- test += "C";
+ deferred.done( function() {
+
+ test += "C";
+ } );
+
+ test += "A";
+
+ }, function() {
+
+ test += "B";
} );
- test += "A";
+ strictEqual( test , "ABC" , "Test done callbacks order" );
- }, function() {
+ deferred = createDeferred();
- test += "B";
- } );
+ deferred.resolveWith( jQuery , [ document ] ).done( function( doc ) {
+ ok( this === jQuery && arguments.length === 1 && doc === document , "Test fire context & args" );
+ });
+
+ // #8421
+ deferred = createDeferred();
+ deferred.resolveWith().done(function() {
+ ok( true, "Test resolveWith can be called with no argument" );
+ });
+ });
+} );
+
+jQuery.each( [ "", " - new operator" ], function( _, withNew ) {
+
+ function createDeferred( fn ) {
+ return withNew ? new jQuery.Deferred( fn ) : jQuery.Deferred( fn );
+ }
+
+ test("jQuery.Deferred" + withNew, function() {
+
+ expect( 8 );
+
+ createDeferred().resolve().then( function() {
+ ok( true , "Success on resolve" );
+ ok( this.isResolved(), "Deferred is resolved" );
+ }, function() {
+ ok( false , "Error on resolve" );
+ }).always( function() {
+ ok( true , "Always callback on resolve" );
+ });
+
+ createDeferred().reject().then( function() {
+ ok( false , "Success on reject" );
+ }, function() {
+ ok( true , "Error on reject" );
+ ok( this.isRejected(), "Deferred is rejected" );
+ }).always( function() {
+ ok( true , "Always callback on reject" );
+ });
+
+ createDeferred( function( defer ) {
+ ok( this === defer , "Defer passed as this & first argument" );
+ this.resolve( "done" );
+ }).then( function( value ) {
+ strictEqual( value , "done" , "Passed function executed" );
+ });
+ });
+} );
- strictEqual( test , "ABC" , "Test done callbacks order" );
+test( "jQuery.Deferred.chain - filtering (done)", function() {
- deferred = jQuery._Deferred();
+ expect(3);
- deferred.resolveWith( jQuery , [ document ] ).done( function( doc ) {
- ok( this === jQuery && arguments.length === 1 && doc === document , "Test fire context & args" );
+ var defer = jQuery.Deferred(),
+ chained = defer.chain(function( a, b ) {
+ return a * b;
+ }),
+ value1,
+ value2,
+ value3;
+
+ chained.done(function( result ) {
+ value3 = result;
+ });
+
+ defer.done(function( a, b ) {
+ value1 = a;
+ value2 = b;
});
- // #8421
- deferred = jQuery._Deferred();
- deferred.resolveWith().done(function() {
- ok( true, "Test resolveWith can be called with no argument" );
+ defer.resolve( 2, 3 );
+
+ strictEqual( value1, 2, "first resolve value ok" );
+ strictEqual( value2, 3, "second resolve value ok" );
+ strictEqual( value3, 6, "result of filter ok" );
+
+ jQuery.Deferred().reject().chain(function() {
+ ok( false, "chain should not be called on reject" );
});
});
-test("jQuery.Deferred()", function() {
+test( "jQuery.Deferred.chain - filtering (fail)", function() {
+
+ expect(3);
- expect( 10 );
+ var defer = jQuery.Deferred(),
+ chained = defer.chain( null, function( a, b ) {
+ return a * b;
+ } ),
+ value1,
+ value2,
+ value3;
- jQuery.Deferred( function( defer ) {
- strictEqual( this , defer , "Defer passed as this & first argument" );
- this.resolve( "done" );
- }).then( function( value ) {
- strictEqual( value , "done" , "Passed function executed" );
+ chained.fail(function( result ) {
+ value3 = result;
});
- jQuery.Deferred().resolve().then( function() {
- ok( true , "Success on resolve" );
- }, function() {
- ok( false , "Error on resolve" );
+ defer.fail(function( a, b ) {
+ value1 = a;
+ value2 = b;
});
- jQuery.Deferred().reject().then( function() {
- ok( false , "Success on reject" );
- }, function() {
- ok( true , "Error on reject" );
+ defer.reject( 2, 3 );
+
+ strictEqual( value1, 2, "first reject value ok" );
+ strictEqual( value2, 3, "second reject value ok" );
+ strictEqual( value3, 6, "result of filter ok" );
+
+ jQuery.Deferred().resolve().chain( null, function() {
+ ok( false, "chain should not be called on resolve" );
+ } );
+});
+
+test( "jQuery.Deferred.chain - deferred (done)", function() {
+
+ expect(3);
+
+ var defer = jQuery.Deferred(),
+ chained = defer.chain(function( a, b ) {
+ return jQuery.Deferred(function( defer ) {
+ defer.reject( a * b );
+ });
+ }),
+ value1,
+ value2,
+ value3;
+
+ chained.fail(function( result ) {
+ value3 = result;
});
- ( new jQuery.Deferred( function( defer ) {
- strictEqual( this , defer , "Defer passed as this & first argument (new)" );
- this.resolve( "done" );
- }) ).then( function( value ) {
- strictEqual( value , "done" , "Passed function executed (new)" );
+ defer.done(function( a, b ) {
+ value1 = a;
+ value2 = b;
});
- ( new jQuery.Deferred() ).resolve().then( function() {
- ok( true , "Success on resolve (new)" );
- }, function() {
- ok( false , "Error on resolve (new)" );
+ defer.resolve( 2, 3 );
+
+ strictEqual( value1, 2, "first resolve value ok" );
+ strictEqual( value2, 3, "second resolve value ok" );
+ strictEqual( value3, 6, "result of filter ok" );
+});
+
+test( "jQuery.Deferred.chain - deferred (fail)", function() {
+
+ expect(3);
+
+ var defer = jQuery.Deferred(),
+ chained = defer.chain( null, function( a, b ) {
+ return jQuery.Deferred(function( defer ) {
+ defer.resolve( a * b );
+ });
+ } ),
+ value1,
+ value2,
+ value3;
+
+ chained.done(function( result ) {
+ value3 = result;
});
- ( new jQuery.Deferred() ).reject().then( function() {
- ok( false , "Success on reject (new)" );
- }, function() {
- ok( true , "Error on reject (new)" );
+ defer.fail(function( a, b ) {
+ value1 = a;
+ value2 = b;
});
- var tmp = jQuery.Deferred();
+ defer.reject( 2, 3 );
- strictEqual( tmp.promise() , tmp.promise() , "Test deferred always return same promise" );
- strictEqual( tmp.promise() , tmp.promise().promise() , "Test deferred's promise always return same promise as deferred" );
+ strictEqual( value1, 2, "first reject value ok" );
+ strictEqual( value2, 3, "second reject value ok" );
+ strictEqual( value3, 6, "result of filter ok" );
});
-test("jQuery.when()", function() {
+test( "jQuery.when" , function() {
expect( 23 );
@@ -166,57 +286,63 @@ test("jQuery.when()", function() {
} , function( message , value ) {
- ok( jQuery.isFunction( jQuery.when( value ).then( function( resolveValue ) {
+ ok( jQuery.isFunction( jQuery.when( value ).done(function( resolveValue ) {
strictEqual( resolveValue , value , "Test the promise was resolved with " + message );
- } ).promise ) , "Test " + message + " triggers the creation of a new Promise" );
+ }).promise ) , "Test " + message + " triggers the creation of a new Promise" );
} );
- ok( jQuery.isFunction( jQuery.when().then( function( resolveValue ) {
+ ok( jQuery.isFunction( jQuery.when().done(function( resolveValue ) {
strictEqual( resolveValue , undefined , "Test the promise was resolved with no parameter" );
- } ).promise ) , "Test calling when with no parameter triggers the creation of a new Promise" );
+ }).promise ) , "Test calling when with no parameter triggers the creation of a new Promise" );
var cache, i;
for( i = 1 ; i < 4 ; i++ ) {
jQuery.when( cache || jQuery.Deferred( function() {
this.resolve( i );
- }) ).then( function( value ) {
+ }) ).done(function( value ) {
strictEqual( value , 1 , "Function executed" + ( i > 1 ? " only once" : "" ) );
cache = value;
- }, function() {
- ok( false , "Fail called" );
});
}
});
-test("jQuery.when() - joined", function() {
-
- expect(8);
-
- jQuery.when( 1, 2, 3 ).done( function( a, b, c ) {
- strictEqual( a , 1 , "Test first param is first resolved value - non-observables" );
- strictEqual( b , 2 , "Test second param is second resolved value - non-observables" );
- strictEqual( c , 3 , "Test third param is third resolved value - non-observables" );
- }).fail( function() {
- ok( false , "Test the created deferred was resolved - non-observables");
- });
-
- var successDeferred = jQuery.Deferred().resolve( 1 , 2 , 3 ),
- errorDeferred = jQuery.Deferred().reject( "error" , "errorParam" );
-
- jQuery.when( 1 , successDeferred , 3 ).done( function( a, b, c ) {
- strictEqual( a , 1 , "Test first param is first resolved value - resolved observable" );
- same( b , [ 1 , 2 , 3 ] , "Test second param is second resolved value - resolved observable" );
- strictEqual( c , 3 , "Test third param is third resolved value - resolved observable" );
- }).fail( function() {
- ok( false , "Test the created deferred was resolved - resolved observable");
- });
-
- jQuery.when( 1 , errorDeferred , 3 ).done( function() {
- ok( false , "Test the created deferred was rejected - rejected observable");
- }).fail( function( error , errorParam ) {
- strictEqual( error , "error" , "Test first param is first rejected value - rejected observable" );
- strictEqual( errorParam , "errorParam" , "Test second param is second rejected value - rejected observable" );
- });
+test("jQuery.when - joined", function() {
+
+ expect(25);
+
+ var deferreds = {
+ value: 1,
+ success: jQuery.Deferred().resolve( 1 ),
+ error: jQuery.Deferred().reject( 0 ),
+ futureSuccess: jQuery.Deferred(),
+ futureError: jQuery.Deferred()
+ },
+ willSucceed = {
+ value: true,
+ success: true,
+ error: false,
+ futureSuccess: true,
+ futureError: false
+ };
+
+ jQuery.each( deferreds, function( id1, defer1 ) {
+ jQuery.each( deferreds, function( id2, defer2 ) {
+ var shouldResolve = willSucceed[ id1 ] && willSucceed[ id2 ],
+ expected = shouldResolve ? [ 1, 1 ] : [ 0, undefined ],
+ code = id1 + "/" + id2;
+ jQuery.when( defer1, defer2 ).done(function( a, b ) {
+ if ( shouldResolve ) {
+ same( [ a, b ], expected, code + " => resolve" );
+ }
+ }).fail(function( a, b ) {
+ if ( !shouldResolve ) {
+ same( [ a, b ], expected, code + " => resolve" );
+ }
+ });
+ } );
+ } );
+ deferreds.futureSuccess.resolve( 1 );
+ deferreds.futureError.reject( 0 );
});
Please sign in to comment.
Something went wrong with that request. Please try again.