Adds always and chain methods to deferreds.

This commit is contained in:
jaubourg 2011-04-07 05:49:32 +02:00
parent 75a4bc4a4e
commit bb99899ca0
2 changed files with 307 additions and 153 deletions

View file

@ -1,7 +1,7 @@
(function( jQuery ) { (function( jQuery ) {
var // Promise methods 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 // Static reference to slice
sliceDeferred = [].slice; sliceDeferred = [].slice;
@ -100,10 +100,38 @@ jQuery.extend({
deferred.done( doneCallbacks ).fail( failCallbacks ); deferred.done( doneCallbacks ).fail( failCallbacks );
return this; return this;
}, },
always: function() {
return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments );
},
fail: failDeferred.done, fail: failDeferred.done,
rejectWith: failDeferred.resolveWith, rejectWith: failDeferred.resolveWith,
reject: failDeferred.resolve, reject: failDeferred.resolve,
isRejected: failDeferred.isResolved, 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 // Get a promise for this deferred
// If obj is provided, the promise aspect is added to the object // If obj is provided, the promise aspect is added to the object
promise: function( obj ) { promise: function( obj ) {

View file

@ -1,153 +1,273 @@
module("deferred", { teardown: moduleTeardown }); 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;
deferred = jQuery._Deferred();
test = false;
deferred.done( function( value ) {
equals( value , "value" , "Test pre-resolve callback" );
test = true;
} );
deferred.resolve( "value" );
ok( test , "Test pre-resolve callbacks called right away" );
test = false;
deferred.done( function( value ) {
equals( value , "value" , "Test post-resolve callback" );
test = true;
} );
ok( test , "Test post-resolve callbacks called right away" );
deferred.cancel();
test = true;
deferred.done( function() {
ok( false , "Cancel was ignored" );
test = false;
} );
ok( test , "Test cancel" );
deferred = jQuery._Deferred().resolve();
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 = ""; test("jQuery._Deferred" + withNew, function() {
deferred = jQuery._Deferred().done( function() {
test += "A"; expect( 11 );
}, function() { var deferred,
object,
test;
test += "B"; deferred = createDeferred();
} ).resolve(); test = false;
strictEqual( test , "AB" , "Test multiple done parameters" ); deferred.done( function( value ) {
equals( value , "value" , "Test pre-resolve callback" );
test = true;
} );
test = ""; deferred.resolve( "value" );
deferred.done( function() { ok( test , "Test pre-resolve callbacks called right away" );
test = false;
deferred.done( function( value ) {
equals( value , "value" , "Test post-resolve callback" );
test = true;
} );
ok( test , "Test post-resolve callbacks called right away" );
deferred.cancel();
test = true;
deferred.done( function() {
ok( false , "Cancel was ignored" );
test = false;
} );
ok( test , "Test cancel" );
deferred = createDeferred().resolve();
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 = "";
deferred = createDeferred().done( function() {
test += "A";
}, function() {
test += "B";
} ).resolve();
strictEqual( test , "AB" , "Test multiple done parameters" );
test = "";
deferred.done( function() { 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" );
});
});
} );
test( "jQuery.Deferred.chain - filtering (done)", function() {
expect(3);
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;
});
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.chain - filtering (fail)", function() {
expect(3);
var defer = jQuery.Deferred(),
chained = defer.chain( null, function( a, b ) {
return a * b;
} ),
value1,
value2,
value3;
chained.fail(function( result ) {
value3 = result;
});
defer.fail(function( a, b ) {
value1 = a;
value2 = b;
});
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" );
} ); } );
strictEqual( test , "ABC" , "Test done callbacks order" );
deferred = jQuery._Deferred();
deferred.resolveWith( jQuery , [ document ] ).done( function( doc ) {
ok( this === jQuery && arguments.length === 1 && doc === document , "Test fire context & args" );
});
// #8421
deferred = jQuery._Deferred();
deferred.resolveWith().done(function() {
ok( true, "Test resolveWith can be called with no argument" );
});
}); });
test("jQuery.Deferred()", function() { test( "jQuery.Deferred.chain - deferred (done)", function() {
expect( 10 ); expect(3);
jQuery.Deferred( function( defer ) { var defer = jQuery.Deferred(),
strictEqual( this , defer , "Defer passed as this & first argument" ); chained = defer.chain(function( a, b ) {
this.resolve( "done" ); return jQuery.Deferred(function( defer ) {
}).then( function( value ) { defer.reject( a * b );
strictEqual( value , "done" , "Passed function executed" ); });
}),
value1,
value2,
value3;
chained.fail(function( result ) {
value3 = result;
}); });
jQuery.Deferred().resolve().then( function() { defer.done(function( a, b ) {
ok( true , "Success on resolve" ); value1 = a;
}, function() { value2 = b;
ok( false , "Error on resolve" );
}); });
jQuery.Deferred().reject().then( function() { defer.resolve( 2, 3 );
ok( false , "Success on reject" );
}, function() {
ok( true , "Error on reject" );
});
( new jQuery.Deferred( function( defer ) { strictEqual( value1, 2, "first resolve value ok" );
strictEqual( this , defer , "Defer passed as this & first argument (new)" ); strictEqual( value2, 3, "second resolve value ok" );
this.resolve( "done" ); strictEqual( value3, 6, "result of filter ok" );
}) ).then( function( value ) {
strictEqual( value , "done" , "Passed function executed (new)" );
});
( new jQuery.Deferred() ).resolve().then( function() {
ok( true , "Success on resolve (new)" );
}, function() {
ok( false , "Error on resolve (new)" );
});
( new jQuery.Deferred() ).reject().then( function() {
ok( false , "Success on reject (new)" );
}, function() {
ok( true , "Error on reject (new)" );
});
var tmp = jQuery.Deferred();
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" );
}); });
test("jQuery.when()", function() { 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;
});
defer.fail(function( a, b ) {
value1 = a;
value2 = b;
});
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" );
});
test( "jQuery.when" , function() {
expect( 23 ); expect( 23 );
@ -166,57 +286,63 @@ test("jQuery.when()", function() {
} , function( message , value ) { } , 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 ); 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" ); 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; var cache, i;
for( i = 1 ; i < 4 ; i++ ) { for( i = 1 ; i < 4 ; i++ ) {
jQuery.when( cache || jQuery.Deferred( function() { jQuery.when( cache || jQuery.Deferred( function() {
this.resolve( i ); this.resolve( i );
}) ).then( function( value ) { }) ).done(function( value ) {
strictEqual( value , 1 , "Function executed" + ( i > 1 ? " only once" : "" ) ); strictEqual( value , 1 , "Function executed" + ( i > 1 ? " only once" : "" ) );
cache = value; cache = value;
}, function() {
ok( false , "Fail called" );
}); });
} }
}); });
test("jQuery.when() - joined", function() { test("jQuery.when - joined", function() {
expect(8); expect(25);
jQuery.when( 1, 2, 3 ).done( function( a, b, c ) { var deferreds = {
strictEqual( a , 1 , "Test first param is first resolved value - non-observables" ); value: 1,
strictEqual( b , 2 , "Test second param is second resolved value - non-observables" ); success: jQuery.Deferred().resolve( 1 ),
strictEqual( c , 3 , "Test third param is third resolved value - non-observables" ); error: jQuery.Deferred().reject( 0 ),
}).fail( function() { futureSuccess: jQuery.Deferred(),
ok( false , "Test the created deferred was resolved - non-observables"); futureError: jQuery.Deferred()
}); },
willSucceed = {
value: true,
success: true,
error: false,
futureSuccess: true,
futureError: false
};
var successDeferred = jQuery.Deferred().resolve( 1 , 2 , 3 ), jQuery.each( deferreds, function( id1, defer1 ) {
errorDeferred = jQuery.Deferred().reject( "error" , "errorParam" ); jQuery.each( deferreds, function( id2, defer2 ) {
var shouldResolve = willSucceed[ id1 ] && willSucceed[ id2 ],
jQuery.when( 1 , successDeferred , 3 ).done( function( a, b, c ) { expected = shouldResolve ? [ 1, 1 ] : [ 0, undefined ],
strictEqual( a , 1 , "Test first param is first resolved value - resolved observable" ); code = id1 + "/" + id2;
same( b , [ 1 , 2 , 3 ] , "Test second param is second resolved value - resolved observable" ); jQuery.when( defer1, defer2 ).done(function( a, b ) {
strictEqual( c , 3 , "Test third param is third resolved value - resolved observable" ); if ( shouldResolve ) {
}).fail( function() { same( [ a, b ], expected, code + " => resolve" );
ok( false , "Test the created deferred was resolved - resolved observable"); }
}); }).fail(function( a, b ) {
if ( !shouldResolve ) {
jQuery.when( 1 , errorDeferred , 3 ).done( function() { same( [ a, b ], expected, code + " => resolve" );
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" ); } );
}); deferreds.futureSuccess.resolve( 1 );
deferreds.futureError.reject( 0 );
}); });