This is a quickie how-to post to help address a couple little gotchas you may run into when implementing LABjs onto your site.
Consider standard page code like this:
<script src="framework.js"></script> <script src="myscript.js"></script> <script> myscript.init(); </script> <script> framework.init(); framework.doSomething(); </script>
In this example, “myscript.init” is a function that is defined in “myscript.js”, and “framework.init” and “framework.doSomething” are both defined inside of “framework.js”.
The first pass at implementing LABjs to load these scripts may seem like this code would work correctly:
<script>
$LAB
.script("framework.js")
.script("myscript.js")
.wait(myscript.init); // won't work!
</script>
<script>
framework.init(); // also won't work
framework.doSomething(); // also won't work
</script>
Why is my function reference undefined?
Firstly, the code “.wait(myscript.init)” seems like this is the correct way to tell LABjs to run “myscript.init” function after the scripts finish loading. But there’s a subtle problem: with regular <script> tags, the inline script block in the first snippet that has “myscript.init()” in it is guaranteed to not even be EVALUATED before the scripts synchronously have loaded before it. So, “myscript.init” is guaranteed to have been defined by that point, so it will work as expected.
But, in the second snippet, we pass a reference to “myscript.init” into the wait() function. But, “myscript.init” hasn’t been defined yet, because the chain for LABjs will execute immediately, as it starting to load the scripts. This means that at the time that .wait(…) first gets parsed, “myscript.init” doesn’t exist. It will be defined, later, when the scripts finish, but the reference is undefined at that point, and thus cannot be used yet.
So, the simple way to fix this problem is this:
<script>
$LAB
.script("framework.js")
.script("myscript.js")
.wait(function(){ myscript.init(); });
</script>
<script>
framework.init();
framework.doSomething();
</script>
So, what we pass to “wait” is not a reference to the as-of-yet-undefined “myscript.init”, but an anoynmous function that will call/execute “myscript.init()” at the proper time. Because of how functions and closures and execution bindings work in JavaScript, this syntax will wait to try to resolve/execute “myscript.init()” until that function is guaranteed to have been loaded (because “myscript.js” finished loading!).
It’s a simple but devious concept in JavaScript… the difference between a function reference “myscript.init” and an anonymous function that will call the intended function later “function(){myscript.init();}”.
OK, but I still have undefined code
For the exact same reason that “myscript.init” wasn’t yet defined when we tried to pass it to .wait(…), the code in the second inline <script> block, the calls to “framework.init()” and “framework.doSomething()”, those scripts do not yet exist (are undefined) at the moment the inline script block in question executes (which would be immediately after the first parse of the $LAB chain, but before either of its scripts have arrived).
So, we have to put THAT code into a .wait(…) as well, so that it waits to be parsed/executed until the right time.
<script>
$LAB
.script("framework.js")
.script("myscript.js")
.wait(function(){
myscript.init();
framework.init();
framework.doSomething();
});
</script>
We could do two separate .wait(…) calls, but that’s unnecessary in this example, so I just combine the two sets of inline code into one wait() function.
Think asynchronously
The key issue is the difference between immediately executing code (like in the original inline <script> tags) and the need to defer asynchronously executing code, by wrapping it in an anonymous function reference. This is because LABjs, by definition, loads scripts asynchronously, as opposed to regular <script> tags which are synchronous.
It may take a couple of tries before you wrap your brain completely around this stuff. But the basic pattern you can default to applying when replacing <script> tags with LABjs calls is this:
- For every <script src=”…”></script> tag you are replacing, you should have a “.script(…)” call
- For every <script>…/*inline code*/…</script> inline script block with code in it, we need a “.wait(function(){ … })” call to wrap around the code
Hopefully that helps clear up some confusion you may run into!
