]> source.dussan.org Git - jquery.git/commitdiff
Adds always and chain methods to deferreds.
authorjaubourg <j@ubourg.net>
Thu, 7 Apr 2011 03:49:32 +0000 (05:49 +0200)
committerjaubourg <j@ubourg.net>
Thu, 7 Apr 2011 03:49:32 +0000 (05:49 +0200)
src/deferred.js
test/unit/deferred.js

index 90f9c8089cee420d4215f8e131bc5f2d110a48fd..feaccaef32159fbdd2ced9470bd0c3b8e223facc 100644 (file)
@@ -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 );
\ No newline at end of file
index 6ba4767a6dee7ac4b3ca7279f29bdb04e3641377..c1ed7c57e1b050967bc02d4cc2307f0d6c319131 100644 (file)
 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 );
 });