Using Deferred Objects to Counter Threading Problems
Recently at my job I had to work out some logic to make absolutely sure that some code didn’t run until all of the JavaScript files were loaded. I’d like to share the solution with you just in case you need it, or more likely when I need it as a reference down the road. However it lands, here we go…
Normally HTML loads JavaScript in sequence, but we had some threading going on and the final init was running first. There’s a saga here, so I’m just going to give you the short version.
We didn’t want all of our JavaScript in one file, so we loaded the modules separately just like we do in development. This revealed a problem with threading on the server side: the little “init” file was loading first because it was small and only contained one line of code. The modules don’t run until told by the init
, but if it shows up early then it will ask for things that don’t exist yet. That’s bad.
Even worse, it will still be a problem when we decide whether to gather all of the code into three files or four or however many. They could possibly load out of order instead of in sequence as the HTML would imply. This looked like a job for Deferred Objects.
Before we go further, let me explain the basic structure of our codebase. We have a bunch of files which create an object inside an closure function and then return it. The result is that a parent object is created with a bunch of methods. Each one leaves the main object alone if it exists, but creates one if it doesn’t.
At this point, it doesn’t matter which one loads first, second or whatever. We can concatenate in any order and split things up any way we like. The only hard rule is that the final init command is executed last, after all of the files are loaded and run.
Here’s a snippet to give you an idea of what the modules look like. There could be a bunch of these, as well as a parent controller module.
After all of that happens and all sorts of groovy modules are added to a central global object, we’re supposed to fire an init
function in the primary parent module. This function will have code which will check for the existence of the various modules and fire init
functions like the one in the snippet above. So at the end of everything, we execute one line of JavaScript to kick off everything.
This must go last, but… thanks to the wonders of threading, it tends to go first. And if we concatenate that sucker into one of three files, we still don’t know which will fire first. What to do?
Well, it occurred to me that we could take advantage of loading jQuery in the head
. I wanted it in the footer, but Mura does stuff in the middle of the page and jQuery needs to be present. Therefore, we can put a script tag into the head where we declare some deferred objects. The logic pattern is in this fiddle. Check out the comments in that and then come back. I’ll wait.
We apply this logical goodness by declaring the deferred objects and the array in the head, along with the done
function that we’ll fire at the end. Then in the footer, we add our JavaScript files just as we normally would. The magic of this solution relies upon Gulp for resolutions and stuff.
I added three files to the mix. Each of them contains just one line, which resolves one of our deferred objects. When Gulp concatenates things, it adds that line to the end of each of the output files. Three deferred objects for three files, and each one resolves when the file is completely loaded and run. Once they’re all in, the final done
function runs and our primary init
function is called.
So it doesn’t matter which file runs first because they’re designed to make functions available without running them until their inits are called, and their inits aren’t called until everything is loaded. Yay!