Skip to content
Permalink
Browse files

Merge 79276be into 53aa87f

  • Loading branch information
gibson042 committed Jan 11, 2015
2 parents 53aa87f + 79276be commit 51850419bfa24ccaaa86114c27d0d31c00aa20d7
Showing with 474 additions and 78 deletions.
  1. +14 −1 Gruntfile.js
  2. +2 −0 package.json
  3. +21 −0 promises-aplus-adapter.js
  4. +166 −17 src/deferred.js
  5. +271 −60 test/unit/deferred.js
@@ -10,6 +10,7 @@ module.exports = function( grunt ) {
}

var gzip = require( "gzip-js" ),
spawn = require( "child_process" ).spawn,
srcHintOptions = readOptionalJSON( "src/.jshintrc" );

// The concatenated file won't pass onevar
@@ -168,12 +169,24 @@ module.exports = function( grunt ) {
});
});

grunt.registerTask( "promises-aplus-tests", function() {
var done = this.async();
spawn( "node", [
"./node_modules/.bin/promises-aplus-tests",
"./promises-aplus-adapter.js"
], {
stdio: "inherit"
}).on( "close", function( code ) {
done( code === 0 );
});
});

// Short list as a high frequency watch task
grunt.registerTask( "dev", [ "build:*:*", "lint" ] );

grunt.registerTask( "test_fast", [ "node_smoke_test" ] );

grunt.registerTask( "test", [ "default", "test_fast" ] );
grunt.registerTask( "test", [ "default", "test_fast", "promises-aplus-tests" ] );

grunt.registerTask( "default", [ "jsonlint", "dev", "uglify", "dist:*", "compare_size" ] );
};
@@ -46,6 +46,8 @@
"jsdom": "1.5.0",
"load-grunt-tasks": "1.0.0",
"npm": "2.1.12",
"promises-aplus-tests": "2.1.0",
"q": "1.1.2",
"qunitjs": "1.16.0",
"requirejs": "2.1.15",
"sinon": "1.12.2",
@@ -0,0 +1,21 @@
"use strict";

require( "jsdom" ).env( "", function (errors, window) {
if (errors) {
console.error(errors);
return;
}
var jQuery = require( "./" )( window );

exports.deferred = function () {
var deferred = jQuery.Deferred();

return {
get promise() {
return deferred.promise();
},
resolve: deferred.resolve.bind(deferred),
reject: deferred.reject.bind(deferred)
};
};
});
@@ -4,14 +4,24 @@ define([
"./callbacks"
], function( jQuery, slice ) {

function Identity( v ) {
return v;
}
function Thrower( ex ) {
throw ex;
}

jQuery.extend({

Deferred: function( func ) {
var tuples = [
// action, add listener, listener list, final state
[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
[ "notify", "progress", jQuery.Callbacks("memory") ]
// 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") ]
],
state = "pending",
promise = {
@@ -22,12 +32,16 @@ jQuery.extend({
deferred.done( arguments ).fail( arguments );
return this;
},
then: function( /* fnDone, fnFail, fnProgress */ ) {
// Keep pipe for back-compat
pipe: function( /* fnDone, fnFail, fnProgress */ ) {
var fns = arguments;

return jQuery.Deferred(function( newDefer ) {
jQuery.each( tuples, function( i, tuple ) {
var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
// deferred[ done | fail | progress ] for forwarding actions to newDefer
// 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 ) ) {
@@ -46,6 +60,123 @@ jQuery.extend({
fns = null;
}).promise();
},
then: function( onFulfilled, onRejected, onProgress ) {
var maxDepth = 0;
function resolve( deferred, handler, depth ) {
return function() {
var that = this === promise ? undefined : this,
args = arguments,
fn = function() {
var returned, then;

// Support: Promises/A+ section 2.3.3.3.3
// https://promisesaplus.com/#point-59
// Ignore double-resolution attempts
if ( depth < maxDepth ) {
return;
}

try {
returned = handler.apply( that, args );

// Support: Promises/A+ section 2.3.1
// https://promisesaplus.com/#point-48
if ( returned === deferred.promise() ) {
throw new TypeError();
}

// Support: Promises/A+ sections 2.3.3.1, 3.5
// https://promisesaplus.com/#point-54
// https://promisesaplus.com/#point-75
// Retrieve `then` only once
then = returned &&

// Support: Promises/A+ section 2.3.4
// https://promisesaplus.com/#point-64
// Only check objects and functions for thenability
( typeof returned === "object" ||
typeof returned === "function" ) &&
returned.then;

if ( jQuery.isFunction( then ) ) {
maxDepth++;
then.call(
returned,
resolve( deferred, Identity, maxDepth ),
resolve( deferred, Thrower, maxDepth ),
deferred.notify
);
} else if ( Identity === handler ) {
deferred.resolveWith( that || deferred.promise(),
args );
} else {
deferred.resolve( returned );
}
} catch ( e ) {

// Support: Promises/A+ section 2.3.3.3.4.1
// https://promisesaplus.com/#point-61
// Ignore post-resolution exceptions
if ( depth + 1 >= maxDepth ) {
if ( Thrower === handler ) {
deferred.rejectWith( that || deferred.promise(),
args );
} else {
deferred.reject( e );
}
}
}
};

// Support: Promises/A+ section 2.3.3.3.1
// https://promisesaplus.com/#point-57
// Re-resolve promises immediately to dodge false rejection from
// subsequent errors
if ( depth ) {
fn();
} else {
setTimeout( fn );
}
};
}

return jQuery.Deferred(function( newDefer ) {
// fulfilled_handlers.add( ... )
tuples[ 0 ][ 3 ].add(
resolve(
newDefer,
jQuery.isFunction( onFulfilled ) ?
onFulfilled :
Identity,
0
)
);

// rejected_handlers.add( ... )
tuples[ 1 ][ 3 ].add(
resolve(
newDefer,
jQuery.isFunction( onRejected ) ?
onRejected :
Thrower,
0
)
);

// progress_handlers.add( ... )
tuples[ 2 ][ 3 ].add(function() {
var that = this,
args = arguments;
setTimeout(function() {
if ( jQuery.isFunction( onProgress ) ) {
args = [ onProgress.apply( that, args ) ];
that = newDefer.promise();
}
newDefer.notifyWith( that, args );
});
});
}).promise();
},
// Get a promise for this deferred
// If obj is provided, the promise aspect is added to the object
promise: function( obj ) {
@@ -54,32 +185,50 @@ jQuery.extend({
},
deferred = {};

// Keep pipe for back-compat
promise.pipe = promise.then;

// Add list-specific methods
jQuery.each( tuples, function( i, tuple ) {
var list = tuple[ 2 ],
stateString = tuple[ 3 ];
stateString = tuple[ 4 ];

// promise[ done | fail | progress ] = list.add
// promise.done = list.add
// promise.fail = list.add
// promise.progress = list.add
promise[ tuple[1] ] = list.add;

// Handle state
if ( stateString ) {
list.add(function() {
// state = [ resolved | rejected ]
state = stateString;
list.add(
function() {
// state = "resolved" (i.e., fulfilled)
// state = "rejected"
state = stateString;
},

// [ reject_list | resolve_list ].disable; progress_list.lock
}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
// rejected_callbacks.disable
// fulfilled_callbacks.disable
tuples[ i ^ 1 ][ 2 ].disable,

// progress_callbacks.lock
tuples[ 2 ][ 2 ].lock
);
}

// deferred[ resolve | reject | notify ]
// fulfilled_handlers.fire
// rejected_handlers.fire
// progress_handlers.fire
list.add( tuple[ 3 ].fire );

// deferred.resolve = function() { deferred.resolveWith(...) }
// deferred.reject = function() { deferred.rejectWith(...) }
// deferred.notify = function() { deferred.notifyWith(...) }
deferred[ tuple[0] ] = function() {
deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
return this;
};

// deferred.resolveWith = list.fireWith
// deferred.rejectWith = list.fireWith
// deferred.notifyWith = list.fireWith
deferred[ tuple[0] + "With" ] = list.fireWith;
});

0 comments on commit 5185041

Please sign in to comment.
You can’t perform that action at this time.