Softlion has indicated several points with which I agree.
Potential problems:
One problem may arise with your current implementation if one of your add-ons calls $doc.trigger("addonready"); synchronously:
// addon 1 : $doc.on('beforedo',function(e){ $doc.trigger('wait'); //do synchronous stuff : $('body').append('<div class="progressbar"></div>'); console.log('addon 1 initialized'); $doc.trigger("addonready"); } // addon 2 : $doc.on('beforedo',function(e){ $doc.trigger('wait'); $.ajax({ ... complete: function(){ console.log('addon 2 initialized'); $doc.trigger("addonready"); } }); }
In this case, depending on the resolution order of your callbacks, you may accidentally fire your readyfordo event after the first addon has activated its addonready function, and before the second one fails to fire wait .
Your code is also based on the assumption that all your add-ons will always execute exactly one .trigger('addonready') for each .trigger('wait') . I don’t know what your code looks like or how many add-ons you have, but ensuring that this is the case for every possible execution path is quite complicated (for example: did you test cases of 2^n failure if you have n ajax calls?)
As long as all your code is inside the house, you can control it, but to me it seems fragile.
jQuery Deferred / Promises:
A common template is to use jQuery promises. All jQuery asynchronous calls are now wrapped in promises, and the library offers an API that allows you to manage them in a rather elegant way - plus it has probably been tested in more complex cases than your own code.
Here are my 2 cents:
$doc.component = { initQueue: [], //this array will contain callbacks, each of which //is expected to return a promise dostg : function () { var queue = $doc.component.initQueue; var promises = []; var i, fnInit; for(i=0; i<queue.length; i++){ fnInit = queue[i]; //safeguard, maybe useless : if (typeof(fnInit) !== 'function') { continue; } var obj = fnInit(); // we stack together all return values in an array : promises.push( obj ); } // wait for all that should be waited for, then trigger your "main" event : $.when.apply($, promises).then(function(){ $doc.trigger('readyfordo'); }); // $.when sorts out between promises and other results, and waits // only if some promises are not resolved. } }; //in component addon files $doc.component.initQueue.push(function(){ //do addon stuff //if waiting for asynch event, return a promise. //examples : // jQuery ajax functions already return a Deferred : return $.ajax({ ... }); // just don't forget the 'return' ... return $.get(url, {...}, function(){ ... }); //or, if you need a custom promise, Softlion example : var deferred = $.Deferred(); doasyncthing(function() { deferred.resolve(); }); return deferred.promise(); });
Instead of using beforedo , wait , addonready , you have plugins that register the function in the initQueue known to your component - note that you can not register a callback if your addon does not need it.
Roughly speaking: in your add-ons you replace $doc.on('beforeDo', function(){ ... }) with $doc.component.initQueue.push(function(){ ... }) , and if you need to wait for something, you return a promise around it.
Then you can let the $.when() function take care of $.when() everything together and waiting for what is expected.
UPDATE: $.when expects Promises as separate arguments
If it is stored in an array, you need to call $.when.apply($, array) to match the signature of the function.
$.when(array) will assume that its argument (array) is not a promise and will immediately resolve.
fiddle