diff --git a/Gruntfile.js b/Gruntfile.js index 58ceea30..3ad611f1 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -27,6 +27,7 @@ module.exports = function(grunt) { "src/manipulation.js", "src/event.js", "src/traversing.js", + "src/deferred.js", "src/outro.js" ], tests: { diff --git a/src/deferred.js b/src/deferred.js new file mode 100644 index 00000000..1715e33b --- /dev/null +++ b/src/deferred.js @@ -0,0 +1,63 @@ + +var oldDeferred = jQuery.Deferred, + tuples = [ + // action, add listener, callbacks, .then handlers, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), + jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), + jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory"), + jQuery.Callbacks("memory") ] + ]; + +jQuery.Deferred = function( func ) { + var deferred = oldDeferred(), + promise = deferred.promise(); + + deferred.pipe = promise.pipe = function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + migrateWarn( "deferred.pipe() is deprecated" ); + + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + deferred[ tuple[1] ](function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this === promise ? newDefer.promise() : this, + fn ? [ returned ] : arguments + ); + } + }); + }); + fns = null; + }).promise(); + + }; + + deferred.isResolved = function() { + migrateWarn( "deferred.isResolved is deprecated" ); + return deferred.state() === "resolved"; + }; + + deferred.isRejected = function() { + migrateWarn( "deferred.isRejected is deprecated" ); + return deferred.state() === "rejected"; + }; + + if ( func ) { + func.call( deferred, deferred ); + } + + return deferred; +}; \ No newline at end of file diff --git a/test/deferred.js b/test/deferred.js new file mode 100644 index 00000000..7616c586 --- /dev/null +++ b/test/deferred.js @@ -0,0 +1,181 @@ + +module("deferred"); + +test( ".pipe() warnings", function( assert ) { + assert.expect( 4 ); + + var d = jQuery.Deferred(), + p = d.promise(); + + function checkValue( v ) { + assert.equal( v, 1, "got correct value" ); + } + + // Deferred + expectWarning( "pipe", function() { + d.pipe( checkValue ); + }); + + // Deferred's promise object + expectWarning( "pipe", function() { + p.pipe( checkValue ); + }); + + // Should happen synchronously for .pipe() + d.resolve( 1 ); +}); + +test( "[PIPE ONLY] jQuery.Deferred.pipe - filtering (fail)", function( assert ) { + + assert.expect( 4 ); + + var value1, value2, value3, + defer = jQuery.Deferred(), + piped = defer.pipe( null, function( a, b ) { + return a * b; + }), + done = jQuery.map( new Array( 3 ), function() { return assert.async(); } ); + + piped.fail(function( result ) { + value3 = result; + }); + + defer.fail(function( a, b ) { + value1 = a; + value2 = b; + }); + + defer.reject( 2, 3 ).pipe( null, function() { + assert.strictEqual( value1, 2, "first reject value ok" ); + assert.strictEqual( value2, 3, "second reject value ok" ); + assert.strictEqual( value3, 6, "result of filter ok" ); + done.pop().call(); + }); + + jQuery.Deferred().resolve().pipe( null, function() { + assert.ok( false, "then should not be called on resolve" ); + }).then( done.pop() ); + + jQuery.Deferred().reject().pipe( null, jQuery.noop ).fail(function( value ) { + assert.strictEqual( value, undefined, "then fail callback can return undefined/null" ); + done.pop().call(); + }); +}); + +test( "[PIPE ONLY] jQuery.Deferred.pipe - deferred (progress)", function( assert ) { + + assert.expect( 3 ); + + var value1, value2, value3, + defer = jQuery.Deferred(), + piped = defer.pipe( null, null, function( a, b ) { + return jQuery.Deferred(function( defer ) { + defer.resolve( a * b ); + }); + }), + done = assert.async(); + + piped.done(function( result ) { + value3 = result; + }); + + defer.progress(function( a, b ) { + value1 = a; + value2 = b; + }); + + defer.notify( 2, 3 ); + + piped.done(function() { + assert.strictEqual( value1, 2, "first progress value ok" ); + assert.strictEqual( value2, 3, "second progress value ok" ); + assert.strictEqual( value3, 6, "result of filter ok" ); + done(); + }); +}); + +test( "[PIPE ONLY] jQuery.Deferred.pipe - context", function( assert ) { + + assert.expect( 7 ); + + var defer, piped, defer2, piped2, + context = {}, + done = jQuery.map( new Array( 4 ), function() { return assert.async(); } ); + + jQuery.Deferred().resolveWith( context, [ 2 ] ).pipe(function( value ) { + return value * 3; + }).done(function( value ) { + assert.strictEqual( this, context, "[PIPE ONLY] custom context correctly propagated" ); + assert.strictEqual( value, 6, "proper value received" ); + done.pop().call(); + }); + + jQuery.Deferred().resolve().pipe(function() { + return jQuery.Deferred().resolveWith(context); + }).done(function() { + assert.strictEqual( this, context, + "custom context of returned deferred correctly propagated" ); + done.pop().call(); + }); + + defer = jQuery.Deferred(); + piped = defer.pipe(function( value ) { + return value * 3; + }); + + defer.resolve( 2 ); + + piped.done(function( value ) { + assert.strictEqual( this, piped, + "default context gets updated to latest promise in the chain" ); + assert.strictEqual( value, 6, "proper value received" ); + done.pop().call(); + }); + + defer2 = jQuery.Deferred(); + piped2 = defer2.pipe(); + + defer2.resolve( 2 ); + + piped2.done(function( value ) { + assert.strictEqual( this, piped2, + "default context updated to latest promise in the chain (without passing function)" ); + assert.strictEqual( value, 2, "proper value received (without passing function)" ); + done.pop().call(); + }); +}); + +test( "isResolved() and isRejected()", function( assert ) { + + assert.expect( 12 ); + + var defer = jQuery.Deferred(); + + expectWarning( "isResolved unresolved", function() { + assert.strictEqual( defer.isResolved(), false, "isResolved pending" ); + }); + + expectWarning( "isRejected unresolved", function() { + assert.strictEqual( defer.isRejected(), false, "isRejected pending" ); + }); + + defer.resolve( 1 ); + + expectWarning( "isResolved resolved", function() { + assert.strictEqual( defer.isResolved(), true, "isResolved resolved" ); + }); + + expectWarning( "isResolved resolved", function() { + assert.strictEqual( defer.isRejected(), false, "isRejected resolved" ); + }); + + defer = jQuery.Deferred().reject( 1 ); + + expectWarning( "isResolved resolved", function() { + assert.strictEqual( defer.isResolved(), false, "isResolved rejected" ); + }); + + expectWarning( "isResolved resolved", function() { + assert.strictEqual( defer.isRejected(), true, "isRejected rejected" ); + }); +}); \ No newline at end of file diff --git a/test/index.html b/test/index.html index 213368fe..3edb40f0 100644 --- a/test/index.html +++ b/test/index.html @@ -40,6 +40,7 @@ +
diff --git a/warnings.md b/warnings.md index d3836fdc..1d267e67 100644 --- a/warnings.md +++ b/warnings.md @@ -148,6 +148,19 @@ $(document).ajaxStart(function(){ $("#status").text("Ajax started"); }); **Solution**: Boolean properties should generally not be passed to `$().attr` at all; replace with `$().prop` unless you truly intend to update the underlying HTML *attribute*. +### JQMIGRATE: deferred.pipe() is deprecated + +**Cause**: The `.pipe()` method on a `jQuery.Deferred` object was deprecated as of jQuery 1.8, when the `.then()` method was changed to perform the same function. + +**Solution**: Change all occurrences of `.pipe()` to `.then()`. + +### JQMIGRATE: deferred.isResolved() is deprecated +### JQMIGRATE: deferred.isRejected() is deprecated + +**Cause**: As of jQuery 1.7, the `isResolved()` and `isRejected` methods of the `jQuery.Deferred` object have been deprecated. They were removed in jQuery 1.8 and are no longer available in later versions. + +**Solution**: To determine the state of a Deferred object, call `deferred.state()` and check for the `"resolved"` or `"rejected"` string values. + ### JQMIGRATE: jQuery.clean() is deprecated **Cause**: `jQuery.buildFragment()` and `jQuery.clean()` are undocumented internal methods. The signature of `jQuery.buildFragment()` was changed in jQuery 1.8 and 1.9, and `jQuery.clean()` was removed in 1.9. However, we are aware of some plugins or other code that may be using them.