× {{alert.msg}} Never ask again
Get notified about new tutorials RECEIVE NEW TUTORIALS

My way of chaining together numerous jQuery deferred objects so they execute in the right order without the need for nesting callbacks.

Zachery Rodgers
Jan 14, 2015
<p>I have never liked the ways I have seen deferred objects chained together to ensure they execute in the proper order so I came up with this solution. I like to use this to chunk out the build process whenever I am initializing a larger web app and need things to happen in a certain order. The best part is it basically wraps a chain of deferred objects in its own <span style="color:rgb(95, 99, 102)">deferred object. Meaning you can use your standard .done(), </span></p> <pre><code class="language-javascript">/********************************************************** * $.DeferredChain * Execute a chain of $.Deferred() objects in linear order ***********************************************************/ ;(function ($) { $.DeferredChain = function (chain, context) { var def = $.Deferred(), int = 0; function onChainItemDone(context, chain, int, def) { int++; if (chain[int] &amp;&amp; typeof chain[int] == 'function') { var $innerChainItem = chain[int].apply(context); $innerChainItem.done(function () { onChainItemDone(this, chain, int, def) }) } else if (typeof chain[int] == 'object') { var innerChainFunc = chain[int].func, innerChainArgs = chain[int].args || [], $innerChainItem = innerChainFunc.apply(context, innerChainArgs); $innerChainItem.done(function () { onChainItemDone(this, chain, int, def) }) } else def.resolveWith(context); }; if (!chain.length) { $.error('DeferredChain must be an array'); def.resolveWith(context); } else if (chain[int] &amp;&amp; typeof chain[int] == 'function') { var $chainItem = chain[int].apply(context); $chainItem.done(function () { onChainItemDone(this, chain, int, def) }); } else if (chain[int] &amp;&amp; typeof chain[int] == 'object') { var innerChainFunc = chain[int].func, innerChainArgs = chain[int].args || [], $chainItem = innerChainFunc.apply(context, innerChainArgs); $chainItem.done(function () { onChainItemDone(this, chain, int, def) }); } else { $.error('DeferredChain Item must be a function'); def.resolveWith(context); } return def.promise(); } }(window.$)); </code></pre> <p>The way this is used is as follows:</p> <pre><code class="language-javascript">$.DeferredChain(chain, context);</code></pre> <p>Where "chain" is an array of functions that return deferred objects and "context" is the context for these functions to execute. aka "this" from within any of those functions. I use the "context" var for passing/storing data to the main object.</p> <p>An example of how I use this to render an app is:</p> <pre><code class="language-javascript">// for a demo of how the chain functions must be _this.demo = function () { var _def = $.Deferred(), _this = this; $.ajax({ url: '/ajax/getDemo' }) .done(function (json) { _this._data = json; _def.resolveWith(_this); }) .fail(function () { _def.resolveWith(_this); }); return _def.promise(); }; // and the chain $.DeferredChain([ _this._demo, _this._init, _this._handle.events, _this._get.scripts, _this._get.modules, _this._render.baseUI, _this._handle.scroll, _this._render.pageUI, _this._update.userVars, ], _this) .done(function () { console.log('$.DeferredChain has finished') }); </code></pre> <p>After many teaks this script has become quite robust and soon I am going to get around to actually publishing it. Until then I hope it can teach some of you a new way to build chained functions to avoid callback hell. </p> <p>My favorite part of using this script is that I can isolate most issues by just commenting out a line in the array. I can chop big apps into little bits without the added complexity of a compiler getting in my way.</p> <p>Questions, comments, and critiques are always welcome :)</p>
comments powered by Disqus