Created
February 6, 2012 15:29
-
-
Save bennadel/1752673 to your computer and use it in GitHub Desktop.
jQuery.whenSync() Plugin For Chaining Asynchronous Callbacks Using Deferred Objects
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>jQuery.whenSync() For Asynchronous, Deferred Chaining</title> | |
<!-- Include jQuery and the whenSync() plugin. --> | |
<script type="text/javascript" src="../jquery-1.7.1.js"></script> | |
<script type="text/javascript" src="./jquery.whensync.js"></script> | |
<script type="text/javascript"> | |
// Get a reference to the core Array slice() method for the | |
// debugging of our ongoing results. | |
var slice = Array.prototype.slice; | |
// Create a utility function that allows us to resolve the | |
// given deferred object after the given amount of time. | |
var resolver = function( deferred, result, timeout ){ | |
// Resolve the deferred in the future. | |
setTimeout( | |
function(){ | |
// Resolve the given deferred. | |
deferred.resolve( result ); | |
}, | |
timeout | |
); | |
}; | |
// -------------------------------------------------- // | |
// -------------------------------------------------- // | |
// Serialize a chain of asynchronous callsbacks. Each one of | |
// these callbacks will receive a Deferred object so that it | |
// can tell the whenSync() method when to move onto the next | |
// asynchronous method in the chain. | |
var asyncChain = $.whenSync( | |
// Asynchronous method. | |
function( deferred ){ | |
// Log the current method context. | |
console.log( "Method 1" ); | |
console.log( ">Results:", slice.call( arguments, 1 ) ); | |
// Reolve this callback (shortly). | |
resolver( deferred, "result1", 500 ); | |
}, | |
// Asynchronous method. | |
function( deferred, result1 ){ | |
// Log the current method context. | |
console.log( "Method 2" ); | |
console.log( ">Results:", slice.call( arguments, 1 ) ); | |
// Reolve this callback (shortly). | |
resolver( deferred, "result2", 1000 ); | |
}, | |
// Asynchronous method. | |
function( deferred, result1, result2 ){ | |
// Log the current method context. | |
console.log( "Method 3" ); | |
console.log( ">Results:", slice.call( arguments, 1 ) ); | |
// Reolve this callback (shortly). | |
resolver( deferred, "result3", 1500 ); | |
} | |
); | |
// Bind to the asynchronous chain. | |
asyncChain.done( | |
function( result1, result2, result3 ){ | |
// Log out all the results. | |
console.log( "Done() Binding" ); | |
console.log( ">Results:", arguments ); | |
} | |
); | |
</script> | |
</head> | |
<body> | |
<!-- Left intentionally blank. --> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Define a sandbox in which the whenSync() plugin can be defined. | |
(function( $ ){ | |
// Define the whenSync() jQuery plugin. This plugin is designed | |
// to take N-number of callbacks. Each callback will be invoked | |
// in order, given a Deferred object as its first invocation | |
// argument. | |
// | |
// callback( Deferred [, result1, result2, resultN] ); | |
// | |
// Additionally, all previous results will be passed as arguments | |
// 2-N of the callback. Subsequent callbacks will not be invoked | |
// until the Deferred object is resolved. | |
$.whenSync = function( /* callbacks */ ){ | |
// Create a master deferred object for the entire validation | |
// process. This will be rejected if ANY of the callback | |
// Deferred objects is rejected. It will be resolved only | |
// after ALL of the callback Deferreds are resolved. | |
var masterDeferred = $.Deferred(); | |
// Create an array to hold the master results. As each | |
// callback is invoked, we are going to pass-through the | |
// aggregate of all the previous results. | |
var masterResults = []; | |
// Create a true array of callback functions (so that we | |
// can make use of the core Array functions). | |
var callbacks = Array.prototype.slice.call( arguments ); | |
// Check to make sure there is at least one callback. If there | |
// are none, then just return a resolved Deferred. | |
if (!callbacks.length){ | |
// Nothing more to do - resolve the master result. | |
masterDeferred.resolve() | |
// Return the promise of the result. | |
return( masterDeferred.promise() ); | |
} | |
// I provide a recursive means to invoke each callback. | |
// I take the given callback to invoke. This callback will be | |
// invoked with the previously resolved master Results. | |
var invokeCallback = function( callback ){ | |
// Create a deferred result for this particular callback. | |
var deferred = $.Deferred(); | |
// Create a promise for our deferred object so that we | |
// can properly bind to the resolve / reject handlers | |
// for the synchronous callback step. | |
deferred.promise().then( | |
function( /* Resolve arguments. */ ){ | |
// Take the current results and add them to the | |
// end of the master results. | |
masterResults = masterResults.concat( | |
Array.prototype.slice.call( arguments ) | |
); | |
// This callback was resolved. Now, let's see if | |
// we have another callback to execute. | |
var nextCallback = callbacks.shift(); | |
// Check for a next callback. | |
if (nextCallback){ | |
// Recusively invoke the callback. | |
return( invokeCallback( nextCallback ) ); | |
} | |
// No more callbacks are available - our chain of | |
// callbacks is complete. We can therefore | |
// consider the entire chain to be resolved. As | |
// such, we can resulve the master deferred. | |
masterDeferred.resolve.apply( | |
masterDeferred, | |
masterResults | |
); | |
}, | |
function( /* Reject arguments */ ){ | |
// This callback was rejected. We cannot proceed | |
// with any more steps in callback chain - we must | |
// reject the master deferred. | |
// Reject the master deferred and pass-through the | |
// rejected results. | |
masterDeferred.reject.apply( | |
masterDeferred, | |
arguments | |
); | |
} | |
); | |
// While the callback is intended to be asynchronous, | |
// let's catch any synchronous errors that happen in the | |
// immediate execution space. | |
try { | |
// Create an invocation arguments collection so that | |
// we can seamlessly pass-through any previously- | |
// resolved result. The Deferred result will always | |
// be the first argument in this argument collection. | |
var callbackArguments = [ deferred ].concat( masterResults ); | |
// Call the callback with the given arguments (the | |
// Deferred result and any previous results). | |
callback.apply( window, callbackArguments ); | |
} catch( syncError ){ | |
// If there was a synchronous error in the callback | |
// that was not caught, let's return the native error. | |
masterDeferred.reject( syncError ); | |
} | |
}; | |
/* END: invokeCallback(){ .. } */ | |
// Invoke the first callback. | |
invokeCallback( callbacks.shift() ); | |
// Return the promise of the master deferred object. | |
return( masterDeferred.promise() ); | |
}; | |
})( jQuery ); | |
// End jQuery plugin. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment